conan icon indicating copy to clipboard operation
conan copied to clipboard

[question] Activating Conanrun.bat in Visual Studio

Open KevDi opened this issue 3 months ago • 7 comments

What is your question?

Hello, i have the following conanfile.py

from sys import version
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, cmake_layout, CMake, CMakeDeps
from conan.tools.files import copy, load
from conan.tools.env import  VirtualRunEnv, VirtualBuildEnv
import os


class myappRecipe(ConanFile):
    name = "myapp"
    # Optional metadata
    license = "MIT Licence"
    author = "kevin"
    url = "myapp"
    description = "myapp"
    topics = ("myapp")

    # Binary configuration
    settings = "os", "compiler", "build_type", "arch"
    options = {
        "shared": [True, False],
        "enable_testing": [True, False]
    }

    default_options = {
        "shared": True,
        "enable_testing": False,
        "poco*:shared": True,
        "poco*:enable_activerecord":False,
        "poco*:enable_activerecord_compiler":False,
        "poco*:enable_apacheconnector":False,
        "poco*:enable_cppparser":False,
        "poco*:enable_crypto":True,
        "poco*:enable_data":False,
        "poco*:enable_data_mysql":False,
        "poco*:enable_data_odbc":False,
        "poco*:enable_data_postgresql":False,
        "poco*:enable_data_sqlite":False,
        "poco*:enable_encodings":False,
        "poco*:enable_json":True,
        "poco*:enable_jwt":False,
        "poco*:enable_mongodb":False,
        "poco*:enable_net":True,
        "poco*:enable_netssl":True,
        "poco*:enable_netssl_win":False,
        "poco*:enable_pagecompiler":False,
        "poco*:enable_pagecompiler_file2page":False,
        "poco*:enable_pdf":False,
        "poco*:enable_pocodoc":False,
        "poco*:enable_prometheus":True,
        "poco*:enable_redis":False,
        "poco*:enable_sevenzip":False,
        "poco*:enable_util":True,
        "poco*:enable_xml":True,
        "poco*:enable_zip":True,
        "librdkafka*:shared":True,
        "librdkafka*:ssl":True,
    }

    generators = "CMakeDeps"

    # Sources are located in the same place as this recipe, copy them to the recipe
    exports_sources = "CMakeLists.txt", "src/*", "cmake/*", "VERSION", "resources/*", "installer/*"

    def set_version(self):
        """Read version from VERSION file"""
        version_file = os.path.join(self.recipe_folder, "VERSION")
        self.version = load(self, version_file).strip() 

    def requirements(self):
        self.requires("poco/1.13.3", transitive_headers=True, transitive_libs=True)
        self.requires("nlohmann_json/3.11.3")
        self.requires("modern-cpp-kafka/2023.03.07")

        if self.options.enable_testing:
            self.test_requires("gtest/1.15.0")

    def layout(self):
        cmake_layout(self)


    def generate(self):
        build_type = str(self.settings.build_type)
        VirtualBuildEnv(self).generate()

        from conan.tools.env import Environment
        env = Environment()
        env.prepend_path("PATH", os.path.join(self.build_folder, "bin", "Release" if build_type == "RelWithDebInfo" or build_type == "MinSizeRel" else build_type))
        # envvars = env.vars(self, scope="run")
        # envvars.save_script("conanrunenv")

        runenv = VirtualRunEnv(self)
        env = runenv.environment()  
        env.append_path("PATH", os.path.join(self.build_folder, "bin", "Release" if build_type == "RelWithDebInfo" or build_type == "MinSizeRel" else build_type))
        runenv.generate()

        tc = CMakeToolchain(self)
        tc.variables["ENABLE_TESTING"] = self.options.enable_testing
        tc.presets_run_environment = env
        tc.generate()        
       
    def build(self):
        """Build the project using CMake"""
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

        if self.options.enable_testing:
            cmake.test()

    def package(self):
        """Package the built artifacts"""
        cmake = CMake(self)
        cmake.install()

        copy(self, "VERSION", src=self.source_folder, dst=os.path.join(self.package_folder, "res"), keep_path=False)

    def package_info(self):
        """Define package information for consumers"""
        self.cpp_info.libs = []

        self.cpp_info.bindirs = ["bin"]

        self.buildenv_info.prepend_path("PATH", os.path.join(self.package_folder, "bin"))
        self.runenv_info.prepend_path("PATH", os.path.join(self.package_folder, "bin")) 

I used it to install the dependencies for my app and i call it like this: conan install . -pr debug -pr:b debug --build=missing-o "&:enable_testing=True" -c tools.env.virtualenv:powershell=pwsh This then creates all the CMake Files along with the CMakePresets.txt under build\generators and the conanbuild.ps1 and conanrun.ps1 . Then i can do the following from the commandline

.\build\generators\conanbuild.ps1
cmake --preset=conan-default
cmake --build build --preset=conan-debug
.\build\generators\deactivate_conanbuild.ps1
.\build\generators\conanrun.ps1
ctest --preset=conan-debug -j1
.\build\generators\deactivate_conanrun.ps1

With that i can build and test my application from the commandline. But i also work on it in Visual Studio for that i open the CMakeFile via Open Folder in Visual Studio. This works so far. I can configure my application and build the different targets. But i'm not able to run/debug the Executable or the Tests Executables beeing build. Mainly because the PATH Variables are not set correctly to point to the Conan Dependencies DLLs and to my builded dlls. My Builded Artifacts (DLLs and Exe) are located under build/bin/debug and my test executables and their resources are located under build/bin/test/<libraryname>/debug Is there a way to either call the conanrun.ps1 from Visual Studio in before or set the PATH Variables? This is the Conan Generated CMakePresets.txt

{
    "version": 3,
    "vendor": {
        "conan": {}
    },
    "cmakeMinimumRequired": {
        "major": 3,
        "minor": 15,
        "patch": 0
    },
    "configurePresets": [
        {
            "name": "conan-default",
            "displayName": "'conan-default' config",
            "description": "'conan-default' configure using 'Visual Studio 17 2022' generator",
            "generator": "Visual Studio 17 2022",
            "cacheVariables": {
                "CMAKE_POLICY_DEFAULT_CMP0091": "NEW"
            },
            "toolset": {
                "value": "v143",
                "strategy": "external"
            },
            "architecture": {
                "value": "x64",
                "strategy": "external"
            },
            "toolchainFile": "generators\\conan_toolchain.cmake",
            "binaryDir": "C:\\Users\\user\\Documents\\Unterlagen\\Projekte\\myapp\\code\\myapp\\build"
        }
    ],
    "buildPresets": [
        {
            "name": "conan-debug",
            "configurePreset": "conan-default",
            "configuration": "Debug",
            "jobs": 16
        }
    ],
    "testPresets": [
        {
            "name": "conan-debug",
            "configurePreset": "conan-default",
            "configuration": "Debug",
            "execution": {
                "jobs": 16
            },
            "environment": {
                "PATH": "C:\\Users\\user\\Documents\\Unterlagen\\Projekte\\myapp\\code\\myapp\\.conan2\\p\\b\\poco60afd9145c2e4\\p\\bin;C:\\Users\\user\\Documents\\Unterlagen\\Projekte\\myapp\\code\\myapp\\.conan2\\p\\b\\librd422af74851d24\\p\\bin;$penv{PATH};C:\\Users\\user\\Documents\\Unterlagen\\Projekte\\myapp\\code\\myapp\\build\\bin\\Debug",
                "OPENSSL_MODULES": "C:\\Users\\user\\Documents\\Unterlagen\\Projekte\\myapp\\code\\myapp\\.conan2\\p\\b\\opens87290d8895557\\p\\lib\\ossl-modules"
            }
        }
    ]
}

and this is the conanrunenv-debug...ps1

Push-Location $PSScriptRoot
"echo `"Restoring environment`"" | Out-File -FilePath "deactivate_conanrunenv-debug-x86_64.ps1"
$vars = (Get-ChildItem env:*).name
$updated_vars = @("PATH", "OPENSSL_MODULES")

foreach ($var in $updated_vars)
{
    if ($var -in $vars)
    {
        $var_value = (Get-ChildItem env:$var).value
        Add-Content "deactivate_conanrunenv-debug-x86_64.ps1" "`n`$env:$var = `"$var_value`""
    }
    else
    {
        Add-Content "deactivate_conanrunenv-debug-x86_64.ps1" "`nif (Test-Path env:$var) { Remove-Item env:$var }"
    }
}
Pop-Location

$env:PATH="$PSScriptRoot\..\..\.conan2\p\b\poco60afd9145c2e4\p\bin;$PSScriptRoot\..\..\.conan2\p\b\librd422af74851d24\p\bin;$env:PATH;$PSScriptRoot\..\..\build\bin\Debug"
$env:OPENSSL_MODULES="$PSScriptRoot\..\..\.conan2\p\b\opens87290d8895557\p\lib\ossl-modules"

Have you read the CONTRIBUTING guide?

  • [x] I've read the CONTRIBUTING guide

KevDi avatar Nov 13 '25 06:11 KevDi

Hi @KevDi

Thanks for your question. First let me give some hints and general feedback:

  • If your package is an application, I'd strongly suggest:
    • dropping the shared option, as that is only valid for libraries, not applications
    • defining package_type = "application" - VirtualBuildEnv(self).generate() explicit is not necessary, that is already happening by default, yo can drop it.
  • Conan already has a conf item tools.build:skip_test that you can use instead of if self.options.enable_testing:. The cmake.test() and cmake.ctest() are alreday no-ops if this conf is enabled. The conf tools.graph:skip_test can also be used to fully avoid the requirement for test_requires
  • The self.buildenv_info.prepend_path("PATH", os.path.join(self.package_folder, "bin")) shouldn't be necessary. The bin folder is already automatically added to the PATH env-var of the consumers. Same for runenv_info.
  • self.cpp_info.bindirs = ["bin"] is already the default too, not necessary.
  • If your package is an application then self.requires("poco/1.13.3", transitive_headers=True, transitive_libs=True) traits transitive_xxx are not necessary.
  • The -pr:b debug is rarely wanted in practice. That would mean that you might be using possible tool_requires like cmake.exe as Debug executables, which is unnecessary. In general, the "build" profile uses Release. It is not a problem if the "host" profile uses Debug.

memsharded avatar Nov 13 '25 11:11 memsharded

For the environment variables issues, I see that the generated CMakePresets.json file contains in its test-presets:

PATH=...C:\\Users\\user\\Documents\\Unterlagen\\Projekte\\myapp\\code\\myapp\\build\\bin\\Debug

So that is looking correct? Isn't that the folder that contains the executables and DLLs? Besides the relativization and usage of $PSScriptRoot base, it seems that both .ps1 file and the CMakePresets.json file contain the same information, so it seems that it should work. Maybe this is an issue/bug of Visual Studio-CMake integration? I have heard from some users that this integration is not always as smooth as desired, and this is why the vast majority of users do not use the Open Folder, but they still generate the VS project on the command line, then open the VS project as a native solution.

memsharded avatar Nov 13 '25 11:11 memsharded

@memsharded i build an exectuable and some dlls in that project (the executable consumes some of those). And it seems it is a problem in Visual Studio because it is not directly calling the presets. So when i check what VS does for example in the configure step it is calling cmake with the toolchainfile and the cmake variables specified in the Preset File. I think only for running the build he is really using the preset because then i can select it from the drop down menu in Visual Studio.

For the Tests he directly runs the created Test Executable. But they are not able to find the DLLs that i have build neither the ones isntalled via conan. Also running the Exectuable from the Visual Studio Debugger did not works because of the mentioned problems with not finding the DLLs. What i can do and what worked is calling the CTest from the Visual Studio Menu which is a bit awkward

On the Commandline it works flawless and i tried a hell of things like hacking in the PATH adjustments in CMake but non seems to be working.

KevDi avatar Nov 13 '25 11:11 KevDi

i build an exectuable and some dlls in that project (the executable consumes some of those). And it seems it is a problem in Visual Studio because it is not directly calling the presets.

But this is something that shouldn't really require Conan for a solution, this is something that is purely internal to the current CMake project, even if you didn't use Conan at all?

The way this is usually managed is by making sure that the .dlls are in the same "bin" directory than the .exe that is requiring them, as in Windows this is enough for the executable to find them. Aren't the dlls in that same directory as the executable?

For the Tests he directly runs the created Test Executable. But they are not able to find the DLLs that i have build neither the ones isntalled via conan.

It is still not clear if you are opening the VS .sln file generated by CMake or you are opening the CMake project with the VS CMake-"open folder" integration. As suggested above, the behavior might not be the same. I'd recommend trying the first one, the generation of the .sln with cmake, then opening the .sln generated file instead of relying on the "Open Folder" CMake integration.

neither the ones isntalled via conan.

Conan CMakeToolchain adds dependencies dlls to the CMAKE_VS_DEBUGGER_ENVIRONMENT PATH environment. I have just tested and it works:

conan new cmake_lib -d name=mypkg
conan create . -o "*:shared=True"
rm -rf "*"
conan new cmake_exe -d name=myapp -d version=0.1 -d requires=mypkg/0.1
conan install . -o "*:shared=True"
cmake --preset conan-default
# open VS solution
# change build type to "Release" in the IDE
# Set myapp as startup project
# launch executable

And runs, finding the DLL of the dependency.

Furthermore, in the command line cmake --preset flow, it might not even be necessary to activate/deactivate the environment scripts as you are doing, as you have seen, the CMakePResets generated already contain environment definition, so running just cmake/ctest might be good enough.

Maybe we are missing some details, so it would be good to have a reproducible scenario like the above:

  • Minimal, no need to use other heavy external dependencies like poco
  • Self-contained and fully reproducible
  • Fully detailed, step by step, and complete

That could help to understand if there is something else that we could be missing.

memsharded avatar Nov 14 '25 11:11 memsharded

@memsharded i tried to constructed a simple example that just uses fmt and gtest as external dependencies. I uploaded it to my github page (https://github.com/KevDi/conan-vs-example).

I run it like this:

conan install . -pr debug --build=missing -o "&:enable_testing=True" 
cmake --preset=conan-default --fresh  

Then i opened up the created sln File in Visual Studio. I can of course build and start the exe Application if i select it as startup Project. But i cannot run the Test Executable because the builded Lib DLL is not placed beside the Testexecutable and there is no Path set to point to the build/bin/Debug

This is for example the Path that has been set in the Debugging Properties of the Test Executable Project in VS:

PATH=C:/Users/user/.conan2/p/b/fmtea952611cb22e/p/bin;C:/Users/djc3ho/.conan2/p/b/gtest299986b120d4f/p/bin;%PATH%

It just contains the dependencies from Conan. But i also need the Path to be something like this: PATH=C:/Users/djc3ho/.conan2/p/b/fmtea952611cb22e/p/bin;C:/Users/djc3ho/.conan2/p/b/gtest299986b120d4f/p/bin;C:\Users\user\Documents\Unterlagen\Projekte\programming\cpp\dummy\build\bin\Debug;%PATH%

KevDi avatar Nov 17 '25 10:11 KevDi

@memsharded A possible solution that i have found is doing something like this in my main CMakeLists.txt:

set(CMAKE_VS_DEBUGGER_ENVIRONMENT "PATH=%PATH%;${CMAKE_RUNTIME_OUTPUT_DIRECTORY};${CONAN_RUNTIME_LIB_DIRS}")

KevDi avatar Nov 17 '25 12:11 KevDi

Then i opened up the created sln File in Visual Studio. I can of course build and start the exe Application if i select it as startup Project. But i cannot run the Test Executable because the builded Lib DLL is not placed beside the Testexecutable and there is no Path set to point to the build/bin/Debug

This is for example the Path that has been set in the Debugging Properties of the Test Executable Project in VS:

Yes, this is what I meant. This is a classic problem with CMake and Visual Projects (or any kind of project that involves DLLs), not even related to Conan. This happens with just a regular CMakeLists.txt project without dependencies and without using Conan. Users typycally address this by:

  • copying the DLLs to the test binary folder, besides the one that exists in the regular bin folder
  • Let the test binaries be in the regular bin folder, not in their own folder, so they can share the DLL
  • Define the PATH via CMAKE_VS_DEBUGGER_ENVIRONMENT
  • Run the tests with other mechanism, like a script or something that injects PATH env-var (like using the Conan generated environment scripts)

memsharded avatar Nov 17 '25 21:11 memsharded