vscode-cmake-tools icon indicating copy to clipboard operation
vscode-cmake-tools copied to clipboard

Using "Launch target" doesn't allow reading files from the project root due to weird cwd

Open nonk123 opened this issue 1 year ago • 3 comments

Brief Issue Summary

Clicking "Launch target" runs the resulting executable with cwd = build/Debug. This means it cannot access e.g. the assets folder in the project root, which is a major inconvenience when multiple people are working on the same project and have to be instructed on copying that directory manually.

I haven't been able to find a cross-platform way to automate this besides using a file(COPY ...) hack or modifying the extension config, but it just feels wrong. Is there a different solution that works with the stock settings.json so anybody could just jump right in?

CMake Tools Diagnostics

No response

Debug Log

No response

Additional Information

No response

nonk123 avatar Sep 30 '24 18:09 nonk123

Hi @nonk123 Thank you for your GitHub issue! To solve your issue, could you try the following four methods? Please let us know if you have any concerns! Thank you!

  1. Modify the Executable's Working Directory Instead of relying on the default behavior, you can configure your launch settings to set the working directory explicitly. This can usually be done in your settings.json or launch configuration file.

For example, in a typical launch configuration for Visual Studio Code, you might add:

"cwd": "${workspaceFolder}", This will set the current working directory to the project root, allowing access to the assets folder without additional work.

  1. Use a Script to Set Up the Environment You could create a simple shell or batch script that handles building and then running the executable with the correct working directory. For example, a run.sh or run.bat script could do something like:
#!/bin/bash
# Build the project
cmake --build build/Debug
# Run the executable with the project root as the cwd
cd build/Debug
./your_executable

This way, team members just need to run the script, and it handles everything for them.

  1. Use Relative Paths in Your Code If your code can be modified, consider using relative paths to access the assets. For example, if your project structure is: bash
/project_root
    /assets
    /build
        /Debug
            your_executable

You can adjust the asset loading code to look for assets relative to the executable's location: cpp std::string assetPath = "../assets/your_asset.file"; 4. Add Asset Copying to the Build Process If you prefer not to change launch settings or rely on scripts, you can automate the copying of the assets directory during the build process using CMake. You can add a command in your CMakeLists.txt like this:

cmake

add_custom_command(TARGET your_target POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    "${CMAKE_SOURCE_DIR}/assets" "${CMAKE_BINARY_DIR}/assets"
)

This way, every time you build, the assets folder will be copied to the build directory.

Yingzi1234 avatar Oct 08 '24 08:10 Yingzi1234

Hi and thanks for your response!

Just to clarify: the project in question is developed on multiple OSes and with different code editors. The issue at hand is only applicable to development with VS Code and this extension.

I wouldn't have any issues building and running my project straight after cloning the repository using the command line:

cd MyProject
cmake -S . -B build
cmake --build build
./build/MyProject

But if I decide to build and run the project from VS Code, I'll need to either:

  1. Manually modify my local configuration (solution 1), which adds an extra step to getting the project to build and run on the dev's side after cloning
  2. Or add a custom copy target (solution 4) that would be redundant for those who don't work on the project in VS Code.

Solutions 2 and 3 don't quite apply here. Solution 4 is probably the best case scenario, but I'd still prefer to avoid an unnecessary copy for command line builds.

But I guess there isn't a "perfect" way to do this if you have to resort to hacks in all cases.

nonk123 avatar Oct 08 '24 17:10 nonk123

@gcampbell-msft Could you take a look at this issue for us? Thank you in advance!

Yingzi1234 avatar Oct 17 '24 09:10 Yingzi1234

@nonk123 To clarify, you're saying that the inconvenience here is that since we launch from the binary directory, accessing items in the assets folder is different when running from CMake Tools versus running it yourself on the command-line? Am I understanding the problem correctly?

gcampbell-msft avatar Dec 17 '24 20:12 gcampbell-msft

Yes, exactly.

On Tue, Dec 17, 2024, 11:04 PM Garrett Campbell @.***> wrote:

@nonk123 https://github.com/nonk123 To clarify, you're saying that the inconvenience here is that since we launch from the binary directory, accessing items in the assets folder is different when running from CMake Tools versus running it yourself on the command-line? Am I understanding the problem correctly?

— Reply to this email directly, view it on GitHub https://github.com/microsoft/vscode-cmake-tools/issues/4103#issuecomment-2549498634, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKOPXI2YG6YUHTTOHWWWEUT2GB7WFAVCNFSM6AAAAABPD4SRKCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKNBZGQ4TQNRTGQ . You are receiving this because you were mentioned.Message ID: @.***>

nonk123 avatar Dec 18 '24 05:12 nonk123

Eh, answered that on autopilot in the morning. Let me clarify again:

The usual CLI workflow for building and running a CMake project would be:

cmake -S . -B build
cmake --build build
./build/OutputBinary

This would use the assets directory from the project source root, no hacks required.

Using the CMake Tools extension, on the other hand, runs the output binary in cwd=build/${Debug,etc...}. This means you cannot just install the extensions and hop onto working on the project without pulling the assets dir into that cwd using a file(COPY) or custom target hack.

This applies to any "internal" file you would put in the project source root directory, like an input.txt file you would read as part of a file IO hello world project. Most beginners at this level wouldn't want to pollute their CMakeLists.txt with extension-specific tricks.

nonk123 avatar Dec 19 '24 14:12 nonk123

@nonk123 Does this change if you use a CMakePresets or change your settings so that the binary directory is simply build? Rather than using the ${Debug}?

Or, does it simply stem from the fact that we run the executable from the binary directory, rather than from the base source directory?

i.e.

cd build
./OutputBinary

vs.

./build/OutputBinary

gcampbell-msft avatar Dec 20 '24 14:12 gcampbell-msft

Or, does it simply stem from the fact that we run the executable from the binary directory, rather than from the base source directory?

That's exactly the problem. I may not have been clear enough in explaining it.

My usecases so far have required accessing files from the source directory, which would be packaged alongside the binary to the end user. The simplest example being a game with its assets directory next to the executable file.

Since the extension runs the target from the binary directory, myself and other devs have had to copy the required files manually or by messing with CMakeLists.txt to automate this. That is the only problem in the VSCode workflow, since CLI devs run from the source directory and everything works just fine (tm).

nonk123 avatar Dec 20 '24 16:12 nonk123

That's still a thing.

Generally, I don't want to write a launch configuration just to set the correct work dir. More interesting, there is an option to set the wd for Debug, but you can't do it for a regular Launch!

Also, the extension disrespects terminal.integrated.cwd option - it creates a new terminal with ~ wd.

trexxet avatar Nov 03 '25 20:11 trexxet

@trexxet Thank you for the detailed information. If there is progress on this issue, we will update here. Thanks for help us build a better CMake Tools!

yanghhhhhhh avatar Nov 06 '25 06:11 yanghhhhhhh

For reference, here's the hack we've had to settle on in one of our projects:

add_custom_target(KlawiaturaPost ALL
    COMMAND ${CMAKE_COMMAND} -E remove_directory $<TARGET_FILE_DIR:Klawiatura>/data
    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/modsrc $<TARGET_FILE_DIR:Klawiatura>/data
    COMMAND_EXPAND_LISTS)
add_dependencies(KlawiaturaPost Klawiatura)

(I know we're renaming modsrc to data here, but that could be mitigated by sourcing assets from ./modsrc if e.g. compiled in debug mode.)

The catch is that this approach works great on Windows, almost as if those remove_directory and copy_directory operations are merged into "prune nonexisting, and copy over new files". But on Linux (in my personal experience) these two operations are treated separately and executed in a sequence, thus taking a noticeable amount of time to complete.

Now imagine if we have not one folder (assets/{gfx,sfx,etc}) but several ({gfx,sfx,etc}), thus needing multiple instances of these copys, AND they're hundreds of megabytes in size...

nonk123 avatar Nov 06 '25 06:11 nonk123

@nonk123 Thanks for the detailed information.

yanghhhhhhh avatar Nov 06 '25 09:11 yanghhhhhhh