This patch adds targets for printf and fprintf to the bazel build.
Additionally, it adds support for the build system to specify where
files should be written for testing purposes. This was necessary to
enable the fprintf test under bazel.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
add DISABLE_INDEX_MODE and DISABLE_WRITE_INT flags, unify the PRINTF_COPTS location, and adjust the libc_function rule to properly handle immutable lists.
To avoid calling getenv on all platforms, I suggest doing this:
- Add a convenience function declaration, say cpp::string make_test_file_path(const char *) in LibcTest.h.
- Add implementations in cmake.cpp and bazel.cpp for CMake and Bazel restpectively. The one in cmake.cpp would just be a passthrough. The one in bazel.cpp will implement mostly the logic you have added in this patch.
- In the CMake target for LibcUnitTest add cmake.cpp and in the one for Bazel, add bazel.cpp.
libc/test/UnitTest/BazelFilePath.cpp | ||
---|---|---|
20 | We should ideally avoid depending on global object initialization. Can we call getenv from inside the function below? | |
libc/test/UnitTest/CmakeFilePath.cpp | ||
21 | Do we really need the testdata/ prefix? As in, can we not just create a file in the same directory as the test? It makes this function a simple pass through. | |
libc/utils/testutils/OwnedString.h | ||
14 ↗ | (On Diff #511557) | Why is this class required? |
utils/bazel/llvm-project-overlay/libc/libc_build_rules.bzl | ||
13 | These should be defined in libc/BUILD.bazel. Also, they should passed to libc_function as defines = PRINTF_COPTS, which then will be passed to tests which depend on those functions which use those defines. | |
utils/bazel/llvm-project-overlay/libc/test/libc_test_rules.bzl | ||
17 ↗ | (On Diff #511557) | Unused? |
address comments and move OwnedString to src/__support/CPP/
libc/utils/testutils/OwnedString.h | ||
---|---|---|
14 ↗ | (On Diff #511557) | This class holds the cpp::string so that it doesn't get deallocated, and is implicitly castable to char *. In the test, the pattern is auto FILE_PATH = libc_make_test_file_path("filepath");, and then the FILE_PATH can be used as a char *. The actual type of FILE_PATH might be char* or OwnedString, but both work as char*. I've also moved this to __support/CPP/ since LibcTest.h says to only include headers from that folder and testutils. |
respond to comments
libc/src/__support/CPP/string.h | ||
---|---|---|
116 ↗ | (On Diff #512969) | no, I added it when I thought OwnedString would need to be templated so that both cpp::string and const char* arguments could be safely handled in the same way. I've removed it. |
libc/utils/testutils/OwnedString.h | ||
14 ↗ | (On Diff #511557) | By "Doesn't get deallocated" I mean that if we made FILE_PATH a const char* and static cast the return value from libc_make_test_file_path to const char* then the cpp::string that libc_make_test_file_path returns would not be stored. The value of FILE_PATH would be set to the pointer to the buffer inside the cpp::string, then the cpp::string would be destructed, freeing that memory. OwnedString solves this problem by owning the cpp::string and its memory, keeping it from being destructed until the end of the function. |
libc/src/__support/CPP/OwnedString.h | ||
---|---|---|
1 ↗ | (On Diff #515070) | This class should live outside of the CPP directory. |
19 ↗ | (On Diff #515070) | You can make this a template class like this: template <typename T> class CString { T str; public: // These constructors can be implemented iff required. CString() = delete; CString(const CString &) = delete; CString(CString &&) = delete; LIBC_INLINE CString(const T &s) : str(s) {} LIBC_INLINE operator const char *() const { return str.data(); } }; template <> class CString<const char *> { const char *str; public: // These constructors can be implemented iff required. CString() = delete; CString(const CString &) = delete; CString(CString &&) = delete; LIBC_INLINE CString(const char *s) : s(str) {} LIBC_INLINE operator const char *() const { return str; } }; The template will work with cpp::string_view and cpp::span also. |
libc/test/src/stdio/fprintf_test.cpp | ||
38 | This comment is for the OwnedString de-allocation problem you have explained. This is working for you because FILE_PATH is actually a variable and not a temporary. Had you used like, const char *FILE_PATH = libc_make_test_file_path(FILENAME); ::FILE *file = printf_test::fopen(FILE_PATH, "w"); on line 40, you would had the same de-allocation problem. Put other way around, if you make FILE_PATH a cpp::string instance, de-allocation will not happen until the end of this test's scope. The ability to implicitly convert it to a const char * value is still desirable, so I have added a comment under OwnedString. |
address comments
libc/src/__support/CPP/OwnedString.h | ||
---|---|---|
1 ↗ | (On Diff #515070) | I can't move it out of CPP because it's included from LibcTest.h and LibcTest.h specifies that it can only include headers from TestUtils and CPP. |
19 ↗ | (On Diff #515070) | I can't make this class a template because the function it's returning from would need to know the template arguments, since they can't be deduced in a function return type. Both the Bazel and CMake version of the function would therefore need to be the same template type. |
address comments
libc/src/__support/CPP/cstring.h | ||
---|---|---|
18 ↗ | (On Diff #516472) | I think so. This class is primarily useful as a read-only string that may be dynamically allocated, but will automatically clean itself up. The primary use for that is as a return value. |
31 ↗ | (On Diff #516472) | the cstr member is used below in the const char* initializer. It allows this to hold either a cpp::string or a const char*. |
libc/src/__support/cstring.h | ||
---|---|---|
21 ↗ | (On Diff #516551) | The comment trail is on a different diff, but let me restart it here. When you have: cpp::string str("abc"); The var str holds a copy of "abc". Which means that str can be destroyed safely without worrying about the cpp::string's desctructor trying to free its resources. So, you shouldn't need cstr and also not need the constructor on line 34. If you have code like this: CString str("abc"); The constructor on line 29 will be called as const char * is implicitly convertible to cpp::string: https://github.com/llvm/llvm-project/blob/main/libc/src/__support/CPP/string.h#L61. The reason I suggested a template is because, in a general case, we should not try to copy const char * strings. You can do that adjustment later. |
29 ↗ | (On Diff #516551) | You should use LIBC_INLINE for code in src/ |
libc/src/__support/cstring.h | ||
---|---|---|
21 ↗ | (On Diff #516551) | In my testing I'm not seeing the behavior you're describing. When I debug fprintf_test.cpp and look at which constructor CmakeFilePath.cpp is calling I see it using the one on line 34. I think what's happening is the compiler sees that the only call to CString is with a const char* and the version on line 29 is getting removed as unused from the final code since it's all inlined. Regardless, it's not constructing a non-trivial cpp::string. |
libc/src/__support/cstring.h | ||
---|---|---|
21 ↗ | (On Diff #516551) |
That is because you have that constructor. Are you facing any problem if you remove that constructor? |
remove unnecessary char* initializer for cstring and rename the header to c_string.h to clarify that this isn't related to cstring (the libc++ header).
We should ideally avoid depending on global object initialization. Can we call getenv from inside the function below?