Skip to content

Better support building the runtime separately [mod] #4752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Sep 20, 2024

Conversation

kinke
Copy link
Member

@kinke kinke commented Sep 12, 2024

Based on #4743.

the-horo and others added 5 commits September 11, 2024 11:37
For custom commands that require a built ldc2 specify only an absolute
path to compiler binary, not the target name. This is because cmake
handles file paths and target dependencies differently. In the case of
a target dependency the dependency doesn't actually apply to the
`add_custom_command`, it only affects future targets.

As an example, we want to built libruntime.a from a D file foo.d. What
we need to describe is:
1. compile foo.o from foo.d (depends on LDC)
2. archive libruntime.a from foo.o

If we use ${LDC_EXE} as a dependency (only the target name) cmake
would generate:
1. compile foo.o from foo.d
2. archive libruntime.a from foo.o (depends on LDC)

Using an absolute path for the LDC dependency does what we want it to do.

Signed-off-by: Andrei Horodniceanu <[email protected]>
D_EXTRA_FLAGS is the intended way to pass flags like -m32 to ldc so
make sure that its value is respected during the druntime tests.

Signed-off-by: Andrei Horodniceanu <[email protected]>
@the-horo
Copy link
Contributor

Look good. With the last commit we respect RT_CFLAGS when invoking the druntime tests which is always a plus.

Other objectives that we may want to achieve, mostly taken from the discussion in the previous PR.

1. respect configure-time CC and CXX

The current makefiles will use the default values from the environment when the tests are run for the C and C++ compiler, basically ignoring what cmake found, or was configured with. This could be fixed by calling make with CC=${CMAKE_C_COMPILER} and CXX=${CMAKE_CXX_COMPILER}

2. (why we can't) use CC to pass arguments

While gmake should cope with CC being a command and arguments (gcc -m32, zig cc, C:\Program Files\my cc.exe -m32, all work) ldc can not. On windows ldc will treat CC literally, respecting spaces, making it possible to specify CC=C:\Program Files\my cc.exe but it will break with C:\Program Files\my cc.exe -m32.

We have no easy way to fix this as changing the driver code is not feasible so we can only declare a separate variable to hold the arguments of CC. To make it easier to follow the variables let's put these arguments in CC_ARGS.

This story only applies to CC, CXX and DMD aren't inspected by ldc so their requirement are only the ones imposed by gmake, i.e. a command and possible arguments. We can discuss if we should do with those the same we are forced to do for CC, so having DMD_ARGS and CXX_ARGS, or using the make convention and passing arguments inside them.

The current code in cmake is closest to using *_ARGS as it already makes the distinction of splitting $(CC) into a ${CMAKE_C_COMPILER} and ${RT_CFLAGS}.

3. divergence of the makefiles from the standard way of passing flags

This is mostly an issue with the makefiles but I think it is still worth discussing as changing the makefiles can benefit us by simplifying the logic we need to carry out.

This is how a make rule for compiling a C program should look like: https://www.gnu.org/prep/standards/html_node/Command-Variables.html#Command-Variables

foo.o: foo.c
     $(CC) -c $(CPPFLAGS) $(target_specific_flags) $(CFLAGS) $<

We don't use CPPFLAGS so we can continue to ignore it. I already explained why we need $(CC) to be split in command and arguments. With these changes our rules should look like:

foo.o: foo.c
     $(CC) $(CC_ARGS) -c $(target_specific_flags) $(CFLAGS) $<

What is important in the above command:

  1. CFLAGS is the preferred way of passing user flags
  2. CFLAGS is freely modifiable, the target has private flags that make sure that it can be built. CFLAGS are just an addition.

Note that it's also acceptable to have:

MY_FLAGS = -Imy_dir -DFOO_BAR $(CFLAGS)
foo.o: foo.c
     $(CC) $(CC_ARGS) -c $(MY_FLAGS) $<

as this is leads to the same command being run.

How does the current makefiles handle these flags:

  1. It uses the standard name CFLAGS for passing its internal flags. Does this affect us that much? Maybe, maybe not. The standards would help if other people had to contribute to the project as the variables would carry a similar meaning to most other projects. Unless there is a good reason for why we won't follow the standard, example being our CC situation as our code would not work on windows if CC contained arguments, I see no point in purposely using the wrong variable name. We can rewrite the makefiles to be more compliant but that takes work so, in the meantime, we can at least try not to introduce more wrongness.
  2. It doesn't have a variable to put arbitrary flags in. This leads to us working against ourselves my modifying variables that we set inside the makefiles, possibly leading to issues with important flags being dropped. Well, technically MODEL_FLAG is available but that is a implementation detail of the makefile so it it's like modifying private variables, not a sign of good code.

What I propose as a fix

1 should be straightforward. So long as there is a RT_CFLAGS or similar variable to pass flags down to CC everything should work.

2 depends on how we want to pass the flags. If we decide to pass RT_CFLAGS as CFLAGS in the rule below:

     $(CC) $(CC_ARGS) -c $(target_specific_flags) $(CFLAGS) $<

we may not need to implement CC_ARGS at all as we won't be using them. If we want to pass RT_CFLAGS in CC_ARGS then we would need to implement support for this. ditto for DMD and CXX but with those we can also pass arguments inside $(DMD) and $(CXX).

3 is the hardest as what I see as the proper solution would require, significantly, rewriting the makefiles. If we want to go with a bandage solution we should at least introduce variables for specifying arbitrary flags, separate from all the variables that are currently in use and make sure that their values are respected in all invocations of the corresponding compiler. In short, I would prefer if

CFLAGS_BASE=${cflags_base} DFLAGS_BASE=${dflags_base} ${linkdl}
used a separate variable for passing down CFLAGS.

@kinke
Copy link
Member Author

kinke commented Sep 13, 2024

  1. respect configure-time CC and CXX

Perfect, that's what I wanted in the first place (and good to include CXX too), but was afraid of breaking your use case of test-time CC env variable. But you're okay with that in #4743 (comment), perfect.

  1. (why we can't) use CC to pass arguments

Wrt. LDC on Windows not supporting a CC env var with flags - I don't think that'd be a deal breaker. E.g., the current proposal here converts the CMake semicolon-separated lists to space-separated lists via a char-replace, so flags containing spaces aren't quoted and would (likely) break the commands. I think it's fine to focus on Posix first, especially wrt. separate runtime builds and extra target-specific flags and wanting to run the druntime integration tests, on Windows.

  1. divergence of the makefiles from the standard way of passing flags

Yeah that's probably an upstream discussion, but I don't see it as a problem. The actual test Makefiles simply want to use a single combined {C,D}FLAGS variable to keep things simple. And the C[++] stuff is hardly used, there are only few interop tests (shared and stdcpp):

  • CFLAGS is used in a single test Makefile, with 4 occurrences. CFLAGS_BASE is unused.
  • CXXFLAGS is unused, but CXXFLAGS_BASE is used in another single test Makefile, with 4 occurrences.
  • DFLAGS is ubiquitous, with some 72 occurrences in test Makefiles.

Edit:

  • CC is used in both 'interop' test Makefiles, a total of 9 usages.
  • CXX in the stdcpp Makefile only, 5 usages.

…tion tests]

To propagate the C[++] compilers at CMake configure-time to test-time.
cmake_minimum_required(VERSION 3.4.3)
project(runtime C ASM CXX) # CXX for integration tests only
Copy link
Member Author

@kinke kinke Sep 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, ${CMAKE_CXX_COMPILER} was empty for a separate runtime build.

FWIW, I've tested this with bin/ldc-build-runtime --ldcSrcDir=$PWD/../ldc --ninja --dFlags="-mtriple;my-triple" --cFlags="-target;my-triple" and looking at ldc-build-runtime.tmp/CTestTestfile.cmake:

add_test(druntime-test-aa-debug "/usr/bin/gmake" "-C" "/home/mkinkelin/dev/ldc/runtime/druntime/test/aa" "ROOT=/home/mkinkelin/dev/ninja-ldc/ldc-build-runtime.tmp/druntime-test-aa-debug" "DMD=" "BUILD=debug" "SHARED=1" "CC=/usr/bin/cc" "CXX=/usr/bin/c++" "DRUNTIME=/home/mkinkelin/dev/ninja-ldc/ldc-build-runtime.tmp/lib/libdruntime-ldc-debug.a" "DRUNTIMESO=/home/mkinkelin/dev/ninja-ldc/ldc-build-runtime.tmp/lib/libdruntime-ldc-debug-shared.so" "CFLAGS_BASE=-target my-triple -Wall -Wl,-rpath,/home/mkinkelin/dev/ninja-ldc/ldc-build-runtime.tmp/lib" "DFLAGS_BASE=-mtriple my-triple" "LINKDL=-L-ldl")

DMD is empty, ldc-build-runtime defines LDC_EXE_FULL, but not LDMD_EXE_FULL; will fix. Edit: Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, ${CMAKE_CXX_COMPILER} was empty for a separate runtime build.

Yeah, that's expected. We need need the CXX language to be added to the project if we use a C++ compiler, even if only during the tests.

FWIW, I've tested this with bin/ldc-build-runtime --ldcSrcDir=$PWD/../ldc --ninja --dFlags="-mtriple;my-triple" --cFlags="-target;my-triple" and looking at ldc-build-runtime.tmp/CTestTestfile.cmake:

My build + tests worked fine as well.

@kinke
Copy link
Member Author

kinke commented Sep 13, 2024

Oh god, now hitting that super-weird CMAKE_C_COMPILER issue on Apple (some deeply nested usr/bin/cc which fails to find any system library apparently) when forwarding it to make: #3901

@the-horo
Copy link
Contributor

Oh god, now hitting that super-weird CMAKE_C_COMPILER issue on Apple (some deeply nested usr/bin/cc which fails to find any system library apparently) when forwarding it to make: #3901

Lovely. The only thing I got from the discussion is that -lpthread is part of some global System library so I don't think I can help with it.

The windows tests are also failing:

C:/Program Files/LLVM/bin/clang-cl.exe /Wall /O2 /FeD:/a/ldc/build/runtime/druntime-test-shared-release/linkD.exe /FoD:/a/ldc/build/runtime/druntime-test-shared-release/linkD.exe.obj src/linkD.c D:/a/ldc/build/runtime/druntime-test-shared-release/lib.lib 
clang-cl: error: cannot specify '/FoD:/a/ldc/build/runtime/druntime-test-shared-release/linkD.exe.obj' when compiling multiple source files

From a quick glance over the ms docs I think this is fixable. It seems I can't directly make a suggestion https://github.com/orgs/community/discussions/9099:

diff --git a/runtime/druntime/test/shared/Makefile b/runtime/druntime/test/shared/Makefile
index 0ba78e6ff9..1c40a7dd91 100644
--- a/runtime/druntime/test/shared/Makefile
+++ b/runtime/druntime/test/shared/Makefile
@@ -139,16 +139,16 @@ else
 endif
 
 $(ROOT)/linkD$(DOTEXE): $(SRC)/linkD.c $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO)
-	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/[email protected],) $< $(ROOT)/lib$(DOTIMPLIB) $(CC_EXTRAS)
+	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/Fo$(ROOT)/,) $< $(ROOT)/lib$(DOTIMPLIB) $(CC_EXTRAS)
 
 $(ROOT)/linkDR$(DOTEXE): $(SRC)/linkDR.c $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO)
-	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/[email protected],) $< $(DRUNTIME_IMPLIB) $(CC_EXTRAS)
+	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/Fo$(ROOT)/,) $< $(DRUNTIME_IMPLIB) $(CC_EXTRAS)
 
 $(ROOT)/loadDR$(DOTEXE): $(SRC)/loadDR.c $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO)
-	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/[email protected],) $< $(CC_EXTRAS)
+	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/Fo$(ROOT)/,) $< $(CC_EXTRAS)
 
 $(ROOT)/host$(DOTEXE): $(SRC)/host.c $(ROOT)/plugin1$(DOTDLL) $(ROOT)/plugin2$(DOTDLL)
-	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/[email protected],) $< $(CC_EXTRAS)
+	$(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $(if $(findstring $(OS),windows),/Fo$(ROOT)/,) $< $(CC_EXTRAS)
 
 $(ROOT)/liblinkdep$(DOTDLL): $(ROOT)/lib$(DOTDLL)
 $(ROOT)/liblinkdep$(DOTDLL): DFLAGS+=-L$(ROOT)/lib$(DOTIMPLIB)

@the-horo
Copy link
Contributor

If we can get the tests to pass I think these changes are pretty much everything that we need and that we can do simply (we're only ignoring RT_LFLAGS). I still want to rewrite the makefiles but that's between me and dmd upstream. If I end up doing it maybe I'll change some of the variable names but the current way of passing flags and CC should still work the same.

@kinke
Copy link
Member Author

kinke commented Sep 13, 2024

The windows tests are also failing:

C:/Program Files/LLVM/bin/clang-cl.exe /Wall /O2 /FeD:/a/ldc/build/runtime/druntime-test-shared-release/linkD.exe /FoD:/a/ldc/build/runtime/druntime-test-shared-release/linkD.exe.obj src/linkD.c D:/a/ldc/build/runtime/druntime-test-shared-release/lib.lib 
clang-cl: error: cannot specify '/FoD:/a/ldc/build/runtime/druntime-test-shared-release/linkD.exe.obj' when compiling multiple source files

Sigh, this seems like a silly clang-cl bug - yeah, we have one C source file and one library as inputs, but common, you're only compiling a single source file, so please accept the desired object file path, as Microsoft's cl.exe does. - Undetected in CI so far because we switch to clang-cl for the CMake config in

-DCMAKE_C_COMPILER=clang-cl ^
-DCMAKE_CXX_COMPILER=clang-cl ^
but don't have any CC env variable set at test-time.

Thanks for looking into a workaround. Unfortunately this is in upstream...

@kinke kinke force-pushed the standalone-runtime2 branch from bc1cfe5 to 074c388 Compare September 13, 2024 15:39
extra_cmake_flags: >-
-DBUILD_LTO_LIBS=ON
-DRT_CFLAGS=-m32
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, this is pretty similar to the Gentoo situation: the CFLAGS env variable used to set up 64-bit clang-cl as C compiler only applies at CMake config-time, affecting the CMake default flags and making it to the builds:

- name: 'Windows x86: Make CMake configure 64-bit clang-cl for 32-bit code emission'
if: runner.os == 'Windows' && inputs.arch == 'x86'
shell: bash
run: |
set -eux
echo "CFLAGS=-m32" >> $GITHUB_ENV
echo "CXXFLAGS=-m32" >> $GITHUB_ENV
echo "ASMFLAGS=-m32" >> $GITHUB_ENV

But now with clang-cl as druntime integration test CC, that -m32 isn't forwarded, and we need to pass it in via RT_CFLAGS explicitly.

[This worked fine with default Microsoft cl C compiler from the environment (PATH), which diverges (with different executables in different dirs) across x64 and x86. clang-cl doesn't infer the bitness from the environment apparently.]

@kinke kinke marked this pull request as ready for review September 13, 2024 20:14
@kinke kinke merged commit 8a30f4d into ldc-developers:master Sep 20, 2024
20 checks passed
@kinke kinke deleted the standalone-runtime2 branch September 20, 2024 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants