def __init__(self, output, runner, display_name="", user=None, channel=None): # an output stream (writeln, info, warn error) self.output = ScopedOutput(display_name, output) self.display_name = display_name # something that can run commands, as os.sytem self._conan_runner = runner self._conan_user = user self._conan_channel = channel self.compatible_packages = [] self._conan_using_build_profile = False self._conan_requester = None from conan.tools.env import Environment self.buildenv_info = Environment() self.runenv_info = Environment() # At the moment only for build_requires, others will be ignored self.conf_info = Conf() self._conan_buildenv = None # The profile buildenv, will be assigned initialize() self._conan_node = None # access to container Node object, to access info, context, deps... self._conan_new_cpp_info = None # Will be calculated lazy in the getter self._conan_dependencies = None self.env_scripts = {} # Accumulate the env scripts generated in order # layout() method related variables: self.folders = Folders() self.cpp = Infos() self.cpp.package.includedirs = ["include"] self.cpp.package.libdirs = ["lib"] self.cpp.package.bindirs = ["bin"] self.cpp.package.resdirs = ["res"] self.cpp.package.builddirs = [""] self.cpp.package.frameworkdirs = ["Frameworks"]
def test_define_append(): env = Environment() env.define("MyVar", "MyValue") env.append("MyVar", "MyValue1") env.append("MyVar", ["MyValue2", "MyValue3"]) assert env.get("MyVar") == "MyValue MyValue1 MyValue2 MyValue3" env = Environment() env.append("MyVar", "MyValue") env.append("MyVar", "MyValue1") env.define("MyVar", "MyValue2") assert env.get("MyVar") == "MyValue2"
def test_compose_combinations(op1, v1, s1, op2, v2, s2, result): env = Environment() if op1 != "unset": getattr(env, op1)("MyVar", v1, s1) else: env.unset("MyVar") env2 = Environment() if op2 != "unset": getattr(env2, op2)("MyVar", v2, s2) else: env2.unset("MyVar") env.compose(env2) assert env.value("MyVar") == result
def test_compose_path_combinations(op1, v1, op2, v2, result): env = Environment() if op1 != "unset": getattr(env, op1+"_path")("MyVar", v1) else: env.unset("MyVar") env2 = Environment() if op2 != "unset": getattr(env2, op2+"_path")("MyVar", v2) else: env2.unset("MyVar") env.compose(env2) assert env.value("MyVar", pathsep=":") == result
def test_compose_path_combinations(op1, v1, op2, v2, result): env = Environment() if op1 != "unset": getattr(env, op1 + "_path")("MyVar", v1) else: env.unset("MyVar") env2 = Environment() if op2 != "unset": getattr(env2, op2 + "_path")("MyVar", v2) else: env2.unset("MyVar") env.compose_env(env2) env = env.vars(ConanFileMock()) assert env._values["MyVar"].get_str("{name}", None, pathsep=":") == result
def test_dict_access(): env = Environment() env.append("MyVar", "MyValue", separator="@") ret = env.vars(ConanFileMock()).items() assert dict(ret) == {"MyVar": "MyValue"} env = Environment() env.prepend("MyVar", "MyValue", separator="@") env_vars = env.vars(ConanFileMock()) assert dict(env_vars.items()) == {"MyVar": "MyValue"} assert env_vars["MyVar"] == "MyValue" env2 = Environment() env2.define("MyVar", "MyValue2") env.compose_env(env2) env_vars = env.vars(ConanFileMock()) assert dict(env_vars.items()) == {"MyVar": "MyValue@MyValue2"} with pytest.raises(KeyError): _ = env_vars["Missing"] # With previous values in the environment env = Environment() env.prepend("MyVar", "MyValue", separator="@") old_env = dict(os.environ) os.environ.update({"MyVar": "PreviousValue"}) env_vars = env.vars(ConanFileMock()) try: assert env_vars["MyVar"] == "MyValue@PreviousValue" finally: os.environ.clear() os.environ.update(old_env) env = Environment() env.append_path("MyVar", "MyValue") old_env = dict(os.environ) os.environ.update({"MyVar": "PreviousValue"}) env_vars = env.vars(ConanFileMock()) try: assert env_vars["MyVar"] == "PreviousValue{}MyValue".format(os.pathsep) with env_vars.apply(): assert os.getenv("MyVar") == "PreviousValue{}MyValue".format( os.pathsep) finally: os.environ.clear() os.environ.update(old_env) assert list(env_vars.keys()) == ["MyVar"] assert dict(env_vars.items()) == {"MyVar": "MyValue"}
def test_compose_combinations(op1, v1, s1, op2, v2, s2, result): env = Environment(ConanFileMock()) if op1 != "unset": getattr(env, op1)("MyVar", v1, s1) else: env.unset("MyVar") env2 = Environment(ConanFileMock()) if op2 != "unset": getattr(env2, op2)("MyVar", v2, s2) else: env2.unset("MyVar") env.compose_env(env2) with environment_append({"MyVar": "MyVar"}): assert env.get("MyVar") == result assert env._values["MyVar"].get_str(ConanFileMock(), "{name}") == result
def test_compose_combinations(op1, v1, s1, op2, v2, s2, result): env = Environment() if op1 != "unset": getattr(env, op1)("MyVar", v1, s1) else: env.unset("MyVar") env2 = Environment() if op2 != "unset": getattr(env2, op2)("MyVar", v2, s2) else: env2.unset("MyVar") env.compose(env2) with environment_append({"MyVar": "MyVar"}): assert env.get("MyVar") == result assert env.var("MyVar").get_str("{name}") == result
def environment(self): env = Environment() # defines if self.ndebug: self.defines.append(self.ndebug) if self.gcc_cxx11_abi: self.defines.append(self.gcc_cxx11_abi) if self.libcxx: self.cxxflags.append(self.libcxx) if self.cppstd: self.cxxflags.append(self.cppstd) if self.arch_flag: self.cxxflags.append(self.arch_flag) self.cflags.append(self.arch_flag) self.ldflags.append(self.arch_flag) if self.build_type_flags: self.cxxflags.extend(self.build_type_flags) self.cflags.extend(self.build_type_flags) if self.fpic: self.cxxflags.append("-fPIC") self.cflags.append("-fPIC") env.append("CPPFLAGS", ["-D{}".format(d) for d in self.defines]) env.append("CXXFLAGS", self.cxxflags) env.append("CFLAGS", self.cflags) env.append("LDFLAGS", self.ldflags) return env
def build_environment(self): """ collects the buildtime information from dependencies. This is the typical use case of build_requires defining information for consumers """ build_env = Environment() # First visit the direct build_requires for build_require in self._conanfile.dependencies.build_requires: # Lower priority, the runenv of all transitive "requires" of the build requires for require in build_require.dependencies.requires: build_env.compose(self._collect_transitive_runenv(require)) # Second, the implicit self information in build_require.cpp_info build_env.compose(self._runenv_from_cpp_info(build_require.cpp_info)) # Finally, higher priority, explicit buildenv_info if build_require.buildenv_info: build_env.compose(build_require.buildenv_info) # Requires in host context can also bring some direct buildenv_info def _collect_transitive_buildenv(d): r = Environment() for child in d.dependencies.requires: r.compose(_collect_transitive_buildenv(child)) # Then the explicit self if d.buildenv_info: r.compose(d.buildenv_info) return r for require in self._conanfile.dependencies.requires: build_env.compose(_collect_transitive_buildenv(require)) # The profile environment has precedence, applied last profile_env = self._conanfile.buildenv build_env.compose(profile_env) return build_env
def environment(self): """ collects the buildtime information from dependencies. This is the typical use case of build_requires defining information for consumers """ # FIXME: Cache value? build_env = Environment() # Top priority: profile profile_env = self._conanfile.buildenv build_env.compose(profile_env) for require, build_require in self._conanfile.dependencies.build.items(): if require.direct: # higher priority, explicit buildenv_info if build_require.buildenv_info: build_env.compose(build_require.buildenv_info) # Lower priority, the runenv of all transitive "requires" of the build requires if build_require.runenv_info: build_env.compose(build_require.runenv_info) # Then the implicit build_env.compose(runenv_from_cpp_info(build_require.cpp_info)) # Requires in host context can also bring some direct buildenv_info for require in self._conanfile.dependencies.host.values(): if require.buildenv_info: build_env.compose(require.buildenv_info) return build_env
def test_windows_case_insensitive(): # Append and define operation over the same variable in Windows preserve order env = Environment() env.define("MyVar", "MyValueA") env.define("MYVAR", "MyValueB") env.define("MyVar1", "MyValue1A") env.append("MYVAR1", "MyValue1B") folder = temp_folder() display_bat = textwrap.dedent("""\ @echo off echo MyVar=%MyVar%!! echo MyVar1=%MyVar1%!! """) with chdir(folder): env.save_bat("test.bat", generate_deactivate=True) save("display.bat", display_bat) cmd = "test.bat && display.bat && deactivate_test.bat && display.bat" out, _ = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True).communicate() out = out.decode() assert "MyVar=MyValueB!!" in out assert "MyVar=!!" in out assert "MyVar1=MyValue1A MyValue1B!!" in out assert "MyVar1=!!" in out
def runenv_from_cpp_info(conanfile, dep, os_name): """ return an Environment deducing the runtime information from a cpp_info """ # FIXME: Remove conanfile arg dyn_runenv = Environment() cpp_info = dep.cpp_info.aggregated_components() pkg_folder = dep.package_folder # FIXME: This code is dead, cpp_info cannot be None if cpp_info is None: # This happens when the dependency is a private one = BINARY_SKIP return dyn_runenv def _handle_paths(paths): result = [] for p in paths: abs_path = os.path.join(pkg_folder, p) if os.path.exists(abs_path): result.append(abs_path) return result if cpp_info.bindirs: # cpp_info.exes is not defined yet dyn_runenv.prepend_path("PATH", _handle_paths(cpp_info.bindirs)) # If it is a build_require this will be the build-os, otherwise it will be the host-os if os_name and not os_name.startswith("Windows"): if cpp_info.libdirs: libdirs = _handle_paths(cpp_info.libdirs) dyn_runenv.prepend_path("LD_LIBRARY_PATH", libdirs) dyn_runenv.prepend_path("DYLD_LIBRARY_PATH", libdirs) if cpp_info.frameworkdirs: dyn_runenv.prepend_path("DYLD_FRAMEWORK_PATH", _handle_paths(cpp_info.frameworkdirs)) return dyn_runenv
def environment(self): flags = GnuDepsFlags(self._conanfile, self.cpp_info) # cpp_flags cpp_flags = [] cpp_flags.extend(flags.include_paths) cpp_flags.extend(flags.defines) # Ldflags ldflags = flags.sharedlinkflags ldflags.extend(flags.exelinkflags) ldflags.extend(flags.frameworks) ldflags.extend(flags.framework_paths) ldflags.extend(flags.lib_paths) # FIXME: Previously we had an argument "include_rpath_flags" defaulted to False ldflags.extend(flags.rpath_flags) # cflags cflags = flags.cflags cxxflags = flags.cxxflags srf = flags.sysroot if srf: cflags.append(srf) cxxflags.append(srf) ldflags.append(srf) env = Environment() env.append("CPPFLAGS", cpp_flags) env.append("LIBS", flags.libs) env.append("LDFLAGS", ldflags) env.append("CXXFLAGS", cxxflags) env.append("CFLAGS", cflags) return env
def environment(self): """ collects the buildtime information from dependencies. This is the typical use case of build_requires defining information for consumers """ # FIXME: Cache value? build_env = Environment() # Top priority: profile profile_env = self._conanfile.buildenv build_env.compose_env(profile_env) build_requires = self._conanfile.dependencies.build.topological_sort for require, build_require in reversed(build_requires.items()): if require.direct: # Only buildenv_info from direct deps is propagated # higher priority, explicit buildenv_info if build_require.buildenv_info: build_env.compose_env(build_require.buildenv_info) # Lower priority, the runenv of all transitive "requires" of the build requires if build_require.runenv_info: build_env.compose_env(build_require.runenv_info) # Then the implicit if hasattr(self._conanfile, "settings_build"): os_name = self._conanfile.settings_build.get_safe("os") else: os_name = self._conanfile.settings.get_safe("os") build_env.compose_env(runenv_from_cpp_info(self._conanfile, build_require, os_name)) # Requires in host context can also bring some direct buildenv_info host_requires = self._conanfile.dependencies.host.topological_sort for require in reversed(host_requires.values()): if require.buildenv_info: build_env.compose_env(require.buildenv_info) return build_env
def environment(self): # TODO: Seems we want to make this uniform, equal to other generators if self._environment is None: flags = GnuDepsFlags(self._conanfile, self._get_cpp_info()) # cpp_flags cpp_flags = [] cpp_flags.extend(flags.include_paths) cpp_flags.extend(flags.defines) # Ldflags ldflags = flags.sharedlinkflags ldflags.extend(flags.exelinkflags) ldflags.extend(flags.frameworks) ldflags.extend(flags.framework_paths) ldflags.extend(flags.lib_paths) # cflags cflags = flags.cflags cxxflags = flags.cxxflags srf = flags.sysroot if srf: cflags.append(srf) cxxflags.append(srf) ldflags.append(srf) env = Environment() env.append("CPPFLAGS", cpp_flags) env.append("LIBS", flags.libs) env.append("LDFLAGS", ldflags) env.append("CXXFLAGS", cxxflags) env.append("CFLAGS", cflags) self._environment = env return self._environment
def _collect_transitive_buildenv(d): r = Environment() for child in d.dependencies.requires: r.compose(_collect_transitive_buildenv(child)) # Then the explicit self if d.buildenv_info: r.compose(d.buildenv_info) return r
def run_in_windows_bash(conanfile, command, cwd=None, env=None): """ Will run a unix command inside a bash terminal It requires to have MSYS2, CYGWIN, or WSL""" if env: # Passing env invalidates the conanfile.environment_scripts env_win = [env] if not isinstance(env, list) else env env_shell = [] else: env_shell = ["conanbuild.sh"] env_win = ["conanbuild.bat"] subsystem = conanfile.conf["tools.microsoft.bash:subsystem"] shell_path = conanfile.conf["tools.microsoft.bash:path"] if not platform.system() == "Windows": raise ConanException("Command only for Windows operating system") if not subsystem or not shell_path: raise ConanException("The config 'tools.microsoft.bash:subsystem' and 'tools.microsoft.bash:path' are " "needed to run commands in a Windows subsystem") if subsystem == MSYS2: # Configure MSYS2 to inherith the PATH msys2_mode_env = Environment(conanfile) _msystem = {"x86": "MINGW32"}.get(conanfile.settings.get_safe("arch"), "MINGW64") msys2_mode_env.define("MSYSTEM", _msystem) msys2_mode_env.define("MSYS2_PATH_TYPE", "inherit") path = os.path.join(conanfile.generators_folder, "msys2_mode.bat") msys2_mode_env.save_bat(path) env_win.append(path) # Needed to change to that dir inside the bash shell wrapped_shell = '"%s"' % shell_path if " " in shell_path else shell_path if env_win: wrapped_shell = environment_wrap_command(conanfile, env_win, shell_path, cwd=conanfile.generators_folder) cwd = cwd or os.getcwd() if not os.path.isabs(cwd): cwd = os.path.join(os.getcwd(), cwd) cwd_inside = unix_path(conanfile, cwd) wrapped_user_cmd = command if env_shell: # Wrapping the inside_command enable to prioritize our environment, otherwise /usr/bin go # first and there could be commands that we want to skip wrapped_user_cmd = environment_wrap_command(conanfile, env_shell, command, cwd=conanfile.generators_folder) inside_command = 'cd "{cwd_inside}" && ' \ '{wrapped_user_cmd}'.format(cwd_inside=cwd_inside, wrapped_user_cmd=wrapped_user_cmd) inside_command = escape_windows_cmd(inside_command) final_command = 'cd "{cwd}" && {wrapped_shell} --login -c {inside_command}'.format( cwd=cwd, wrapped_shell=wrapped_shell, inside_command=inside_command) conanfile.output.info('Running in windows bash: %s' % final_command) return conanfile._conan_runner(final_command, output=conanfile.output, subprocess=True)
def _collect_transitive_runenv(self, d): r = Environment() for child in d.dependencies.requires: r.compose(self._collect_transitive_runenv(child)) # Apply "d" runenv, first the implicit r.compose(self._runenv_from_cpp_info(d.cpp_info)) # Then the explicit if d.runenv_info: r.compose(d.runenv_info) return r
def _parse_output(self, option): executable = self._conanfile.conf.get("tools.gnu:pkg_config", default="pkg-config") command = [executable, '--' + option, self._library, '--print-errors'] try: env = Environment() if self._pkg_config_path: env.prepend_path("PKG_CONFIG_PATH", self._pkg_config_path) with env.vars(self._conanfile).apply(): return check_output_runner(command).strip() except subprocess.CalledProcessError as e: raise ConanException('pkg-config command %s failed with error: %s' % (command, e))
def __init__(self, output, runner, display_name="", user=None, channel=None): # an output stream (writeln, info, warn error) self.output = ScopedOutput(display_name, output) self.display_name = display_name # something that can run commands, as os.sytem self._conan_runner = runner self._conan_user = user self._conan_channel = channel self.compatible_packages = [] self._conan_using_build_profile = False self._conan_requester = None self.layout = Layout() self.buildenv_info = Environment() self.runenv_info = Environment() self._conan_buildenv = None # The profile buildenv, will be assigned initialize() self._conan_node = None # access to container Node object, to access info, context, deps... self.virtualenv = True # Set to false to opt-out automatic usage of VirtualEnv self._conan_new_cpp_info = None # Will be calculated lazy in the getter
def test_compose(): env = Environment() env.define("MyVar", "MyValue") env.define("MyVar2", "MyValue2") env.define("MyVar3", "MyValue3") env.define("MyVar4", "MyValue4") env.unset("MyVar5") env2 = Environment() env2.define("MyVar", "MyNewValue") env2.append("MyVar2", "MyNewValue2") env2.prepend("MyVar3", "MyNewValue3") env2.unset("MyVar4") env2.define("MyVar5", "MyNewValue5") env.compose(env2) assert env.value("MyVar") == "MyValue" assert env.value("MyVar2") == 'MyValue2' assert env.value("MyVar3") == 'MyValue3' assert env.value("MyVar4") == "MyValue4" assert env.value("MyVar5") == ''
def environment(self): env = Environment() # defines if self.ndebug: self.defines.append(self.ndebug) if self.gcc_cxx11_abi: self.defines.append(self.gcc_cxx11_abi) if self.libcxx: self.cxxflags.append(self.libcxx) if self.cppstd: self.cxxflags.append(self.cppstd) if self.arch_flag: self.cxxflags.append(self.arch_flag) self.cflags.append(self.arch_flag) self.ldflags.append(self.arch_flag) if self.build_type_flags: self.cxxflags.extend(self.build_type_flags) self.cflags.extend(self.build_type_flags) if self.build_type_link_flags: self.ldflags.extend(self.build_type_link_flags) if self.fpic: self.cxxflags.append("-fPIC") self.cflags.append("-fPIC") if self.msvc_runtime_flag: self.cxxflags.append(self.msvc_runtime_flag) self.cflags.append(self.msvc_runtime_flag) if is_msvc(self._conanfile): env.define("CXX", "cl") env.define("CC", "cl") # FIXME: Previously these flags where checked if already present at env 'CFLAGS', 'CXXFLAGS' # and 'self.cxxflags', 'self.cflags' before adding them for f in list(filter(bool, [self.apple_isysroot_flag, self.apple_arch_flag, self.apple_min_version_flag])): self.cxxflags.append(f) self.cflags.append(f) self.ldflags.append(f) env.append("CPPFLAGS", ["-D{}".format(d) for d in self.defines]) env.append("CXXFLAGS", self.cxxflags) env.append("CFLAGS", self.cflags) env.append("LDFLAGS", self.ldflags) return env
def env(): # Append and define operation over the same variable in Windows preserve order env = Environment() env.define("MyVar", "MyValueA") env.define("MYVAR", "MyValueB") env.define("MyVar1", "MyValue1A") env.append("MYVAR1", "MyValue1B") env.define("MyVar2", "MyNewValue2") env = env.vars(ConanFileMock()) env._subsystem = WINDOWS return env
def env(): env = Environment() env.define("MyVar", "MyValue") env.define("MyVar1", "MyValue1") env.append("MyVar2", "MyValue2") env.prepend("MyVar3", "MyValue3") env.unset("MyVar4") env.define("MyVar5", "MyValue5 With Space5=More Space5;:More") env.append("MyVar6", "MyValue6") # Append, but previous not existing env.define_path("MyPath1", "/Some/Path1/") env.append_path("MyPath2", ["/Some/Path2/", "/Other/Path2/"]) env.prepend_path("MyPath3", "/Some/Path3/") env.unset("MyPath4") return env
def __init__(self, output, runner, display_name="", user=None, channel=None, requester=None): # an output stream (writeln, info, warn error) self.output = ScopedOutput(display_name, output) self.display_name = display_name # something that can run commands, as os.sytem self._conan_runner = runner self._conan_user = user self._conan_channel = channel self.compatible_packages = [] self._conan_using_build_profile = False self._conan_requester = requester self.layout = Layout() self.buildenv_info = Environment() self.runenv_info = Environment() self._conan_buildenv = None # The profile buildenv, will be assigned initialize()
def runenv_from_cpp_info(cpp_info): """ return an Environment deducing the runtime information from a cpp_info """ dyn_runenv = Environment() if cpp_info is None: # This happens when the dependency is a private one = BINARY_SKIP return dyn_runenv if cpp_info.bin_paths: # cpp_info.exes is not defined yet dyn_runenv.prepend_path("PATH", cpp_info.bin_paths) # If it is a build_require this will be the build-os, otherwise it will be the host-os if cpp_info.lib_paths: dyn_runenv.prepend_path("LD_LIBRARY_PATH", cpp_info.lib_paths) dyn_runenv.prepend_path("DYLD_LIBRARY_PATH", cpp_info.lib_paths) if cpp_info.framework_paths: dyn_runenv.prepend_path("DYLD_FRAMEWORK_PATH", cpp_info.framework_paths) return dyn_runenv
def test_public_access(): env = Environment(ConanFileMock()) env.define("MyVar", "MyValue") env.append("MyVar", "MyValue2") env.define_path("MyPath", "c:/path/to/something") env.append_path("MyPath", "D:/Otherpath") for name, values in env.items(): if name == "MyVar": assert values == "MyValue MyValue2" if name == "MyPath": assert values == "c:/path/to/something{}D:/Otherpath".format( os.pathsep) env.remove("MyPath", "D:/Otherpath") assert env.get("MyPath") == "c:/path/to/something"
def environment(self): """ collects the runtime information from dependencies. For normal libraries should be very occasional """ runenv = Environment() # FIXME: Missing profile info # FIXME: Cache value? host_req = self._conanfile.dependencies.host test_req = self._conanfile.dependencies.test for _, dep in list(host_req.items()) + list(test_req.items()): if dep.runenv_info: runenv.compose(dep.runenv_info) runenv.compose(runenv_from_cpp_info(dep.cpp_info)) return runenv
def run_environment(self): """ collects the runtime information from dependencies. For normal libraries should be very occasional """ runenv = Environment() # At the moment we are adding "test-requires" (build_requires in host context) # to the "runenv", but this will be investigated for build_require in self._conanfile.dependencies.build_requires: if build_require.context == CONTEXT_HOST: runenv.compose(self._collect_transitive_runenv(build_require)) for require in self._conanfile.dependencies.requires: runenv.compose(self._collect_transitive_runenv(require)) # FIXME: Missing profile info result = runenv return result