This is an archive of the discontinued LLVM Phabricator instance.

[libc][bazel] add file printf targets and support
ClosedPublic

Authored by michaelrj on Mar 27 2023, 3:19 PM.

Details

Summary

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.

Diff Detail

Event Timeline

michaelrj created this revision.Mar 27 2023, 3:19 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptMar 27 2023, 3:19 PM
michaelrj requested review of this revision.Mar 27 2023, 3:19 PM
michaelrj updated this revision to Diff 509483.Mar 29 2023, 3:02 PM

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:

  1. Add a convenience function declaration, say cpp::string make_test_file_path(const char *) in LibcTest.h.
  2. 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.
  3. In the CMake target for LibcUnitTest add cmake.cpp and in the one for Bazel, add bazel.cpp.
michaelrj updated this revision to Diff 511557.Apr 6 2023, 4:26 PM

rewrite to make the test file path system more generic.

michaelrj retitled this revision from [libc][bazel] add printf and fprintf targets to [libc][bazel] add file printf targets and support.Apr 6 2023, 4:26 PM
michaelrj edited the summary of this revision. (Show Details)
sivachandra added inline comments.Apr 12 2023, 12:31 AM
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?

michaelrj updated this revision to Diff 512969.Apr 12 2023, 2:02 PM
michaelrj marked 4 inline comments as done.

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*.
We can't just have FILE_PATH be a cpp::string because cpp::string isn't implicitly castable to char*, and I'm trying to avoid needing a static cast every time the FILE_PATH is used.
Similarly, the FILE_PATH can't just be a char* since if we don't store the string it gets deallocated.
The original intent of this class was for FILE_PATH to always be an OwnedString, which is why it's a template, but that's not necessary now.

I've also moved this to __support/CPP/ since LibcTest.h says to only include headers from that folder and testutils.

libc/src/__support/CPP/string.h
116 ↗(On Diff #512969)

Is this part of std::string, with the explicit tag?

libc/utils/testutils/OwnedString.h
14 ↗(On Diff #511557)

So, two things you say:

  1. Doesn't get deallocated.
  2. Implicitly castable to char *.

I get #2 but can you explain #1 more?

michaelrj updated this revision to Diff 515070.Apr 19 2023, 1:30 PM
michaelrj marked an inline comment as done.

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.

sivachandra added inline comments.Apr 19 2023, 2:17 PM
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.

michaelrj marked an inline comment as done.

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.

sivachandra added inline comments.Apr 24 2023, 11:22 AM
libc/src/__support/CPP/OwnedString.h
1 ↗(On Diff #515070)

That comment should be fixed. The previous testutils now live in __support.

libc/src/__support/CPP/cstring.h
18 ↗(On Diff #516472)

Is this comment correct?

31 ↗(On Diff #516472)

Whats the idea behind keeping a cstr member?

michaelrj updated this revision to Diff 516551.Apr 24 2023, 3:19 PM
michaelrj marked 3 inline comments as done.

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*.

sivachandra added inline comments.Apr 24 2023, 3:37 PM
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/

michaelrj marked an inline comment as done.Apr 24 2023, 5:07 PM
michaelrj added inline comments.
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.

michaelrj updated this revision to Diff 516586.Apr 24 2023, 5:07 PM

move to LIBC_INLINE

sivachandra added inline comments.Apr 24 2023, 5:14 PM
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.

That is because you have that constructor. Are you facing any problem if you remove that constructor?

michaelrj updated this revision to Diff 516972.Apr 25 2023, 4:14 PM

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).

sivachandra accepted this revision.Apr 26 2023, 2:24 PM
This revision is now accepted and ready to land.Apr 26 2023, 2:24 PM
This revision was landed with ongoing or failed builds.Apr 26 2023, 2:31 PM
This revision was automatically updated to reflect the committed changes.