Skip to content

Set CC and CXX environment variables for clang toolchain #1169

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

betamaxbandit
Copy link
Contributor

Sets the C and C++ compiler environment variables so CMake uses Clang instead of the system default compiler.

Fixes #1140

@betamaxbandit
Copy link
Contributor Author

Thanks to @Kummallinen for the suggestion to set cc environment variables.

I'll update this soon with details of how to test.

* set(CMAKE_C_COMPILER clang)
* set(CMAKE_CXX_COMPILER clang++)
*/
addIfAbsent("cc", "clang", envVarsNew); //$NON-NLS-1$ //$NON-NLS-2$
Copy link
Contributor Author

Choose a reason for hiding this comment

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

cc and cxx should be upper case!

Copy link
Contributor

Choose a reason for hiding this comment

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

Does envVarsNew already include the system environment, or is it just the environment set by other parts of Core Build? If it includes the system environment then we cant use addIfAbsent as they will likely already be set if you're on Linux or macOS

Copy link
Contributor Author

@betamaxbandit betamaxbandit Jun 2, 2025

Choose a reason for hiding this comment

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

Does envVarsNew already include the system environment,

No; its the env from the core build and/or any env vars set by the user for a "User Defined Toolchain".

private static IEnvironmentVariable[] addClangEnvVars(IEnvironmentVariable[] envVars) {
List<IEnvironmentVariable> envVarsNew = new ArrayList<>(Arrays.asList(envVars));
/*
* Set CC and CXX environment variables for clang and clang++. This is equivalent to setting these in the
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we also need to set the ASM environment variable?

Copy link
Contributor Author

@betamaxbandit betamaxbandit Jun 2, 2025

Choose a reason for hiding this comment

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

Do we also need to set the ASM environment variable?

Good question. I do not think so. CMake appears to set ASM automatically to clang when CC is set to clang.

I tested this using the attached project.
cmakeASM.zip

In the screendshot below, it can be seen that the ASM is set to clang.exe.

cmakeASM helloworld example

@betamaxbandit
Copy link
Contributor Author

Tests

Test cases
A) Toolchain set to clang, causes the clang compiler to be used automatically.
B) Toolchain set to gcc, uses the default gcc compiler automatically.
Tests should be performed on Linux & Windows.

Prerequisites
Make sure cmake, make and ninja and multiple toolchains are installed on the host, including clang. See [1].

To test
Test Case A) Toolchain set to clang, causes the clang compiler to be used automatically.
Create a CMake Hello World project
From the Launch Bar, open the Launch Configuration (gear icon)
Select Build Settings tab and in the Toolchain dropdown, change to clang toolchain and click OK.
From the Launch Bar, build the project.
Expected: The project builds successfully and in the Console build output it can be seen that the C and CXX compiler are identified as "Clang".

The following shows an example of Console build output:

Configuring in: C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw\build\cmake.debug.win32.x86_64.Local
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw
-- The C compiler identification is Clang 19.1.7
-- The CXX compiler identification is Clang 19.1.7
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/ucrt64/bin/clang.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/ucrt64/bin/clang++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (3.1s)
-- Generating done (0.1s)
-- Build files have been written to: C:/Users/a5107948/cdt-main20250110b/runtime-cdtruntime_clean/cmake_hw/build/cmake.debug.win32.x86_64.Local
Building in: C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw\build\cmake.debug.win32.x86_64.Local
cmake --build . --target all
[1/2] Building CXX object CMakeFiles/cmake_hw.dir/cmake_hw.cpp.obj
[2/2] Linking CXX executable cmake_hw.exe
Build complete (0 errors, 0 warnings): C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw\build\cmake.debug.win32.x86_64.Local

Test case B) Toolchain set to gcc, uses the default gcc compiler automatically.
Continuing from test above, open the Build Settings tab and change the toolchain to gcc and click OK.
Delete the project's build folder.
From the Launch Bar, build the project.
Expected: The project builds successfully and in the Console build output it can be seen that the C and CXX compiler are identified as "GNU".

The following shows an example of Console build output:

Configuring in: C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw\build\cmake.debug.win32.x86_64.Local
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw
-- The C compiler identification is GNU 14.2.0
-- The CXX compiler identification is GNU 14.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/ucrt64/bin/cc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/ucrt64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (3.2s)
-- Generating done (0.1s)
-- Build files have been written to: C:/Users/a5107948/cdt-main20250110b/runtime-cdtruntime_clean/cmake_hw/build/cmake.debug.win32.x86_64.Local
Building in: C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw\build\cmake.debug.win32.x86_64.Local
cmake --build . --target all
[1/2] Building CXX object CMakeFiles/cmake_hw.dir/cmake_hw.cpp.obj
[2/2] Linking CXX executable cmake_hw.exe
Build complete (0 errors, 0 warnings): C:\Users\a5107948\cdt-main20250110b\runtime-cdtruntime_clean\cmake_hw\build\cmake.debug.win32.x86_64.Local

[1]: Before you begin
C/C++ Development User Guide > Before you begin
https://github.com/eclipse-cdt/cdt/blob/abe4d1b8e16b03ceafbeb25f063cb728c7487862/doc/org.eclipse.cdt.doc.user/concepts/cdt_c_before_you_begin.htm

Comment on lines 72 to 73
* However, on Windows, existing cc/cxx envVars with lower-case names will have their names changed to upper case;
* the value remains unchanged.<br>
Copy link
Member

Choose a reason for hiding this comment

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

Why do this - can you simply use same logic as on Linux? See code comment for why I am asking the question

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In our meeting on Th 5 Jun, we discussed this and agreed on windows any lower case cc/cxx env vars should be changed to upper case.
Do you no longer think that is a good idea?

Otherwise, yes, the same logic as used on Linux can be used for Windows.

Comment on lines 94 to 95
iterator.remove();
envVars.add(new EnvironmentVariable(name, evOldValue));
Copy link
Member

Choose a reason for hiding this comment

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

Goes with comment on line 73 and I have a suggestion attached to line 81-111

When you do the remove + add you may be changing the type and logic of the variable. The removed one may not be an EnvironmentVariable but one of the other implementations of of IEnvironmentVariable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I see that now. Renaming the variable name is tricky. Perhaps we should forget about it.

Comment on lines 81 to 153
if (Platform.OS_WIN32.equals(Platform.getOS())) {
// On Windows: case-insensitive check ignores case.
Iterator<IEnvironmentVariable> iterator = envVars.iterator();
boolean found = false;

while (iterator.hasNext()) {
IEnvironmentVariable ev = iterator.next();
if (ev.getName().equalsIgnoreCase(name)) {
found = true;
// Replace if existing name is in lower-case and new name is in upper-case
if (!ev.getName().equals(name) && name.equals(name.toUpperCase())) {
// Original value is respected.
String evOldValue = ev.getValue();
iterator.remove();
envVars.add(new EnvironmentVariable(name, evOldValue));
}
break;
}
}

if (!found) {
envVars.add(new EnvironmentVariable(name, value));
}

} else {
// On non-Windows: case-sensitive check respects case (exact match).
boolean isAbsent = envVars.stream().noneMatch(ev -> ev.getName().equals(name));
if (isAbsent) {
envVars.add(new EnvironmentVariable(name, value));
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (Platform.OS_WIN32.equals(Platform.getOS())) {
// On Windows: case-insensitive check ignores case.
Iterator<IEnvironmentVariable> iterator = envVars.iterator();
boolean found = false;
while (iterator.hasNext()) {
IEnvironmentVariable ev = iterator.next();
if (ev.getName().equalsIgnoreCase(name)) {
found = true;
// Replace if existing name is in lower-case and new name is in upper-case
if (!ev.getName().equals(name) && name.equals(name.toUpperCase())) {
// Original value is respected.
String evOldValue = ev.getValue();
iterator.remove();
envVars.add(new EnvironmentVariable(name, evOldValue));
}
break;
}
}
if (!found) {
envVars.add(new EnvironmentVariable(name, value));
}
} else {
// On non-Windows: case-sensitive check respects case (exact match).
boolean isAbsent = envVars.stream().noneMatch(ev -> ev.getName().equals(name));
if (isAbsent) {
envVars.add(new EnvironmentVariable(name, value));
}
}
boolean isAbsent = envVars.stream().noneMatch(ev -> {
if (Platform.OS_WIN32.equals(Platform.getOS())) {
return ev.getName().equalsIgnoreCase(name);
} else {
return ev.getName().equals(name);
}
});
if (isAbsent) {
envVars.add(new EnvironmentVariable(name, value));
}

@betamaxbandit
Copy link
Contributor Author

Hi @jonahgraham ,
I have pushed an update which addresses your comments.

Sets the C (CC) and C++ (CXX) compiler environment variables so the
choice of toolchain selected in the Build Settings tab is respected.

Toolchains supported are GNU GCC and Clang.

Added JUnit tests for GCCToolChain and ClangToolChain.
Added test document describing manual test steps.

Fixes eclipse-cdt#1140
Copy link
Member

@jonahgraham jonahgraham left a comment

Choose a reason for hiding this comment

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

LGTM

public void clangExe() throws Exception {
IToolChain tc = new ClangToolChain(null, Paths.get("a/clang.exe"), null, null);
IEnvironmentVariable[] variables = tc.getVariables();
assertThat(Arrays.asList(variables), hasItems( //
Copy link
Member

Choose a reason for hiding this comment

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

Nice use of assertions here 😄

Comment on lines +154 to +155
IEnvironmentVariable[] envVars = { new EnvironmentVariable("CC", "CCtestvalue") };
IToolChain tc = new ClangToolChain(null, Paths.get("a/clang.exe"), null, envVars);
Copy link
Member

Choose a reason for hiding this comment

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

This is certainly an interesting case. FTR if an integrator is doing this and they aren't happy with the decision you made here it is on them to resolve it in their code by providing CC and CXX environment variables.

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.

CMake toolchain selection from LaunchBar apparently ignored
5 participants