# variables for cross-platform code?

**URL:** https://discourse.cmake.org/t/variables-for-cross-platform-code/10115
**Category:** Usage
**Created:** [February 16, 2024, 4:11pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115 "2024-02-16T16:11:11Z")
**Posts on this page:** 18
**Page:** 1

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 16, 2024, 4:11pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/1 "2024-02-16T16:11:11Z")

</div>

I find myself writing this kind of thing repeatedly.

```auto
    if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
      set(path_separator ";")
    else()
      set(path_separator ":")
    endif()

    if(CMAKE_HOST_SYSTEM_NAME STREQUAL "DARWIN")
      set(library_path_variable "DYLD_LIBRARY_PATH")
    elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
      set(library_path_variable "PATH")
    else()
      set(library_path_variable "LD_LIBRARY_PATH")
    endif()

```

Surely CMake supplies these variables somewhere, I thought, but I can’t find them. Am I missing something?

---

<div class="post-metadata">

### Author: ![scivision](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/a87d85/32.png) [@scivision](https://discourse.cmake.org/u/scivision)
#### Post date: [February 16, 2024, 5:20pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/2 "2024-02-16T17:20:04Z")

</div>

Part of the issue may be the considerable fanout of such logic due to the number of operating systems known to CMake: [https://cmake.org/cmake/help/latest/variable/CMAKE\_SYSTEM\_NAME.html#system-names-known-to-cmake](https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html#system-names-known-to-cmake)

Of course there is a lot of overlap effectively down to perhaps 3-4 cases as you have.

I think making a CMAKE\_PATHSEP variable would be straightforward, if the CMake maintainers are open to such a contribution. It would possibly help deduplicate internal CMake code as well.

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 16, 2024, 8:25pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/3 "2024-02-16T20:25:30Z")

</div>

I could at best submit an imperfect patch; I don’t know what the values should be for Cygwin and MSYS, and for cross-builds I imagine we need a second variable for the target system. And all that aside, I tried poking into the CMake sources and I don’t see where such things are declared. So, I could give it a shot if I had lots of hints…

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 17, 2024, 6:54pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/4 "2024-02-17T18:54:16Z")

</div>

The separator can at least be dropped by using `cmake -E env --modify` or the `ENVIRONMENT_MODIFICATION` test property.

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 17, 2024, 7:32pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/5 "2024-02-17T19:32:11Z")

</div>

When I read them, the docs for those things didn’t _seem_ to indicate support for adding elements to list-like environment variables (e.g. `PATH` on Windows, where `;` is the separator). Did I misunderstand?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 18, 2024, 3:10am UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/6 "2024-02-18T03:10:19Z")

</div>

Do the `path_list_*` operators need documentation clarifications? I suppose some frontmatter on what “types” are supported would be wise…

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 18, 2024, 3:51am UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/7 "2024-02-18T03:51:46Z")

</div>

Ah, well no, not that, but I only looked at the cmake man page where `-E env` is documented, which doesn’t mention these operators. There’s no obvious direction that I should go look up the `ENVIRONMENT_MODIFICATION` test property (not everyone is reading the HTML docs).

```auto
              --modify ENVIRONMENT_MODIFICATION
                     New in version 3.25.

                     Apply a single ENVIRONMENT_MODIFICATION operation to the
                     modified environment.

                     The NAME=VALUE and --unset=NAME options are equivalent to
                     --modify NAME=set:VALUE and --modify NAME=unset:,
                     respectively. Note that --modify NAME=reset: resets NAME
                     to the value it had when cmake launched (or unsets it),
                     not to the most recent NAME=VALUE option.

              -- New in version 3.24.

                     Added support for the double dash argument --. Use -- to
                     stop interpreting options/environment variables and treat
                     the next argument as the command, even if it start with -
                     or contains a =.

```

So would that get me out of having to use an escaped semicolon in [this](https://gitlab.kitware.com/cmake/cmake/-/issues/25686) example?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 18, 2024, 4:05am UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/8 "2024-02-18T04:05:25Z")

</div>

> [@dabrahams](#):
>
> There’s no obvious direction that I should go look up the `ENVIRONMENT_MODIFICATION` test property (not everyone is reading the HTML docs).

Ah, the link is dropped from the `--help` docs 😕 . Can you file an issue to improve wording to at least encourage looking there?

> [@dabrahams](#):
>
> So would that get me out of having to use an escaped semicolon in [this](https://gitlab.kitware.com/cmake/cmake/-/issues/25686) example?

Yes, semicolon quoting test properties through a generic API was the straw that finally made me say “bah, I just need to implement this in `cmake -E env`”. 🙂 .

Note that:

```auto
cmake -E env --modify \
  PATH=path_list_prepend:path1 \
  PATH=path_list_prepend:path2

```

works as you expect (`path2:path1:$PATH`) so that you can build these up piecemeal instead of having to collate them ahead of time (sorting may be required though).

---

<div class="post-metadata">

### Author: ![starball](https://discourse.cmake.org/user_avatar/discourse.cmake.org/starball/32/3437_2.png) [@starball](https://discourse.cmake.org/u/starball)
#### Post date: [February 21, 2024, 12:25am UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/9 "2024-02-21T00:25:02Z")

</div>

fyi: [https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:SHELL\_PATH](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:SHELL_PATH)

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [February 21, 2024, 11:51pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/10 "2024-02-21T23:51:30Z")

</div>

> [@ben.boeckel](#):
>
> Ah, the link is dropped from the `--help` docs 😕 . Can you file an issue to improve wording to at least encourage looking there?

[https://gitlab.kitware.com/cmake/cmake/-/issues/25703](https://gitlab.kitware.com/cmake/cmake/-/issues/25703)

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [March 5, 2024, 12:51am UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/12 "2024-03-05T00:51:41Z")

</div>

> [@ben.boeckel](#):
>
> Note that:
> 
> ```auto
> cmake -E env --modify \
> PATH=path_list_prepend:path1 \
> PATH=path_list_prepend:path2
> 
> ```
> 
> works as you expect (`path2:path1:$PATH`) so that you can build these up piecemeal instead of having to collate them ahead of time (sorting may be required though).

So, I tried this, and I’m not convinced it’s an improvement:

```diff

modified cmake/modules/FindSwiftXCTest.cmake
@@ -125,9 +125,9 @@ function(add_swift_xctest test_target testee)
       OUTPUT ${test_main}
       # If the executable target depends on DLLs their directories need to be injected into the PATH
       # or they won't be found and the target will fail to run, so invoke it through cmake. Because
- COMMAND
- ${CMAKE_COMMAND} -E env
- "PATH=$<JOIN:$<TARGET_RUNTIME_DLL_DIRS:GenerateXCTestMain>;$ENV{PATH},;>"
+ COMMAND ${CMAKE_COMMAND} -E env --modify
+ "$<LIST:TRANSFORM,$<TARGET_RUNTIME_DLL_DIRS:GenerateXCTestMain>,PREPEND,PATH=path_list_prepend:>"
         --
         $<TARGET_FILE:GenerateXCTestMain> -o ${test_main} ${sources}
       DEPENDS ${sources} GenerateXCTestMain
@@ -141,16 +141,10 @@ function(add_swift_xctest test_target testee)
     add_test(NAME ${test_target}
       COMMAND ${test_target})
 
- # When $ENV{PATH} is interpreted as a list on Windows, trailing backslashes in elements
- # (e.g. "C:\path\to\foo\;C:\another\path\...") will end up causing semicolons to be escaped.
- string(REPLACE "\\" "/" path "$ENV{PATH}")
-
- # Escape the semicolons when forming the environment setting. As explained in
- # https://stackoverflow.com/a/59866840/125349, “this is not the last place the list will be used
- # (and interpreted by CMake). [It] is then used to populate CTestTestfile.cmake, which is later
- # read by CTest to setup your test environment.”
     set_tests_properties(${test_target}
- PROPERTIES ENVIRONMENT "PATH=$<JOIN:$<TARGET_RUNTIME_DLL_DIRS:${test_target}>;${path},\\;>")
+ PROPERTIES ENVIRONMENT_MODIFICATION
+ # For each DLL dependency X, make the PATH=path_list_prepend:X environment modification.
+ "$<LIST:TRANSFORM,$<TARGET_RUNTIME_DLL_DIRS:${test_target}>,PREPEND,PATH=path_list_prepend:>")
 

```

In the 2nd case it’s clearly a huge win, although the repetition in `PREPEND,PATH=path_list_prepend:` is a little baffling until you figure it out. I fear the first case is still not actually correct: to make it robust I’d need to generate surrounding quotes around each of the arguments to `-E env --modify`. Generating one set of quotes surrounding the wholesale setting of `PATH` was easy, but making this correct would require two additional levels of generator expression AFAICT. The problem is that the simple version will often pass testing because of local conditions (no spaces in path names). I only point this out because real-world usage seems to indicate the new feature might need some adjustment.

Using the `SHELL_PATH` expression in the first case, as suggested by @starball, might be a slight improvement in explicitness. Since there are no DLLs on any platform but Windows, though, the existing code worked everywhere.

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 29, 2024, 11:29am UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/13 "2024-03-29T11:29:12Z")

</div>

> [@dabrahams](#):
>
> `+ $<$<LIST:LENGTH,$<TARGET_RUNTIME_DLL_DIRS:GenerateXCTestMain>>:--modify>`

Can `--modify` not be unconditionally passed?

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [March 30, 2024, 6:10pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/14 "2024-03-30T18:10:56Z")

</div>

> [@ben.boeckel](#):
>
> Can `--modify` not be unconditionally passed?

It’s been a while, so I don’t remember why I did that. I’d guess that when the list is empty if you pass `--modify` cmake complains, but I can’t reproduce that now, so I’ll update the example, thanks. I think my concern about the correctness of that code and the complexity of the correct code stands, unless… wait, I see a `VERBATIM` argument to `add_custom_command`. Could that be used to handle the quoting problem? /cc @ben.boeckel

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [April 10, 2024, 10:16pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/15 "2024-04-10T22:16:52Z")

</div>

> [@dabrahams](#):
>
> I see a `VERBATIM` argument to `add_custom_command`. Could that be used to handle the quoting problem? /cc @ben.boeckel

Possibly. I’ve never found `VERBATIM` to be necessary, but it apparently solves problems. Maybe shell/CMake quoting rules are just too deeply ingrained in me at this point…

---

<div class="post-metadata">

### Author: ![dabrahams](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dabrahams/32/4265_2.png) [@dabrahams](https://discourse.cmake.org/u/dabrahams)
#### Post date: [April 15, 2024, 8:10pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/16 "2024-04-15T20:10:20Z")

</div>

Most people avoid paths with spaces in them, so mostly we never find out what’s actually necessary for code to be robust in the face of such paths.

---

<div class="post-metadata">

### Author: ![KUGA2](https://discourse.cmake.org/user_avatar/discourse.cmake.org/kuga2/32/4968_2.png) [@KUGA2](https://discourse.cmake.org/u/KUGA2)
#### Post date: [March 5, 2025, 3:21pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/17 "2025-03-05T15:21:59Z")

</div>

> Note that:  
> cmake -E env --modify   
> PATH=path\_list\_prepend:path1   
> PATH=path\_list\_prepend:path2  
> works as you expect (`path2:path1:$PATH`)

Sorry to dig out this old thread, but this does not work:

```shell
user@PC26280 ~ $ cmake --version
cmake version 3.25.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).
user@PC26280 ~ $ cmake -E env --modify PATH=path_list_prepend:path1 PATH=path_list_prepend:path2 -- cmake -E environment
No such file or directory
user@PC26280 ~ $ cmake -E env --modify PATH=path_list_prepend:path1 PATH=path_list_prepend:path2 -- $(which cmake) -E environment | grep PATH
PATH=path_list_prepend:path2

```

First try breaks PATH so that it does not find cmake anymore. So i added the which to show the path. What am i doing wrong?

---

<div class="post-metadata">

### Author: ![Angew](https://discourse.cmake.org/user_avatar/discourse.cmake.org/angew/32/229_2.png) [@Angew](https://discourse.cmake.org/u/Angew)
#### Post date: [March 5, 2025, 3:32pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/18 "2025-03-05T15:32:48Z")

</div>

`--modify` takes just one argument, so you have to add it before each modification operation. The following worked for me:

```auto
cmake -E env --modify PATH=path_list_prepend:path1 --modify PATH=path_list_prepend:path2 -- cmake -E environment

```

---

<div class="post-metadata">

### Author: ![KUGA2](https://discourse.cmake.org/user_avatar/discourse.cmake.org/kuga2/32/4968_2.png) [@KUGA2](https://discourse.cmake.org/u/KUGA2)
#### Post date: [March 5, 2025, 5:09pm UTC](https://discourse.cmake.org/t/variables-for-cross-platform-code/10115/19 "2025-03-05T17:09:54Z")

</div>

Since this cost my quite some headache, i will leave this snippet here for future readers.  
It is pretty hard to use this correctly within cmake custom command:

```auto
set(SHARED_LIB_SEARCH_DIR_ENV_VAR "PATH") # or LD_LIBRARY_PATH
set(PREPEND_WITH $<TARGET_RUNTIME_DLL_DIRS:mytarget>) # Empty in Linux

add_custom_command(
   OUTPUT ...
                              # Add dummy, in case we got an empty PREPEND_WITH list
   COMMAND ${CMAKE_COMMAND} -E env DUMMY=dummy "$<LIST:TRANSFORM,${PREPEND_WITH},PREPEND,--modify;${SHARED_LIB_SEARCH_DIR_ENV_VAR}=path_list_prepend:>"
      -- "$<TARGET_FILE:mytarget>"
   COMMAND_EXPAND_LISTS
   DEPENDS mytarget
   VERBATIM
)

```
