def test_conf_error_per_package(): text = "*:core:verbosity=minimal" c = ConfDefinition() with pytest.raises( ConanException, match=r"Conf '\*:core:verbosity' cannot have a package pattern"): c.loads(text)
def test_conf_list_operations(text1, text2, expected): c1 = ConfDefinition() c1.loads(text1) c2 = ConfDefinition() c2.loads(text2) c1.update_conf_definition(c2) assert c1.get(text1.split("=")[0]) == expected
def test_conf_get_check_type_and_default(): text = textwrap.dedent("""\ user.company.cpu:jobs=5 user.company.build:ccflags=--m otherflag user.company.list:objs=[0, 1, 2, 3, 4, 'mystr', {'a': 1}, 5, 6] user.company.network:proxies={'url': 'http://api.site.com/apiv2', 'dataType': 'json', 'method': 'GET'} zlib:user.company.check:shared=! zlib:user.company.check:shared_str="False" zlib:user.company.check:static_str=off user.company.list:newnames+=myname """) c = ConfDefinition() c.loads(text) assert c.get("user.company.cpu:jobs", check_type=int) == 5 assert c.get("user.company.cpu:jobs", check_type=str) == "5" # smart conversion with pytest.raises(ConanException) as exc_info: c.get("user.company.cpu:jobs", check_type=list) assert "[conf] user.company.cpu:jobs must be a list-like object." in str( exc_info.value) # Check type does not affect to default value assert c.get("non:existing:conf", default=0, check_type=dict) == 0 assert c.get("zlib:user.company.check:shared") is None # unset value assert c.get("zlib:user.company.check:shared_str") == '"False"' assert c.get("zlib:user.company.check:shared_str", check_type=bool) is False # smart conversion assert c.get("zlib:user.company.check:static_str") == "off" assert c.get("zlib:user.company.check:static_str", check_type=bool) is False # smart conversion assert c.get("user.company.list:newnames") == ["myname" ] # Placeholder is removed
def conf_definition(): text = textwrap.dedent("""\ tools.microsoft.msbuild:verbosity=minimal user.company.toolchain:flags=someflags""") c = ConfDefinition() c.loads(text) return c, text
def test_sdk(): conanfile = ConanFileMock() conf = ConfDefinition() conf.loads("tools.apple:sdk_path=mypath") conanfile.conf = conf conanfile.settings = MockSettings({"os": "Macos", "os.sdk": "macosx"}) xcodebuild = XcodeBuild(conanfile) xcodebuild.build("app.xcodeproj") # sdk_path takes preference assert "SDKROOT=mypath " in conanfile.command conf = ConfDefinition() conanfile.conf = conf xcodebuild = XcodeBuild(conanfile) xcodebuild.build("app.xcodeproj") assert "SDKROOT=macosx " in conanfile.command conanfile.settings = MockSettings({ "os": "Macos", "os.sdk": "macosx", "os.sdk_version": "12.1" }) xcodebuild = XcodeBuild(conanfile) xcodebuild.build("app.xcodeproj") assert "SDKROOT=macosx12.1 " in conanfile.command conanfile.settings = MockSettings({}) xcodebuild = XcodeBuild(conanfile) xcodebuild.build("app.xcodeproj") assert "SDKROOT" not in conanfile.command
def test_conf_get_different_type_input_objects(text, expected): """ Testing any possible Python-evaluable-input-format introduced by the user """ c = ConfDefinition() c.loads(text) assert c.get(text.split("=")[0]) == expected
def _apply_inner_profile(doc, base_profile): """ :param doc: ConfigParser object from the current profile (excluding includes and vars, and with values already replaced) :param base_profile: Profile inherited, it's used as a base profile to modify it. :return: None """ def get_package_name_value(item): """Parse items like package:name=value or name=value""" packagename = None if ":" in item: tmp = item.split(":", 1) packagename, item = tmp result_name, result_value = item.split("=", 1) result_name = result_name.strip() result_value = unquote(result_value) return packagename, result_name, result_value for setting in doc.settings.splitlines(): setting = setting.strip() if setting and not setting.startswith("#"): if "=" not in setting: raise ConanException("Invalid setting line '%s'" % setting) package_name, name, value = get_package_name_value(setting) if package_name: base_profile.package_settings[package_name][name] = value else: base_profile.settings[name] = value if doc.build_requires: # FIXME CHECKS OF DUPLICATED? for req in doc.build_requires.splitlines(): _load_single_build_require(base_profile, req) if doc.tool_requires: for req in doc.tool_requires.splitlines(): _load_single_build_require(base_profile, req) if doc.options: base_profile.options.update(OptionsValues.loads(doc.options)) # The env vars from the current profile (read in doc) # are updated with the included profiles (base_profile) # the current env values has priority current_env_values = EnvValues.loads(doc.env) current_env_values.update(base_profile.env_values) base_profile.env_values = current_env_values if doc.conf: new_prof = ConfDefinition() new_prof.loads(doc.conf, profile=True) base_profile.conf.update_conf_definition(new_prof) if doc.buildenv: buildenv = ProfileEnvironment.loads(doc.buildenv) base_profile.buildenv.update_profile_env(buildenv)
def test_none(): c = ConfDefinition() c.loads(textwrap.dedent("""\ """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) max_cpu_count = msbuild_max_cpu_count_cmd_line_arg(conanfile) assert max_cpu_count is None
def conanfile(): c = ConfDefinition() c.loads(textwrap.dedent("""\ tools.build:jobs=10 """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) return conanfile
def test_conf_skip_test(self): conf = ConfDefinition() conf.loads("tools.build:skip_test=1") conanfile = ConanFileMock() conanfile.settings = Settings() conanfile.conf = conf.get_conanfile_conf(None) meson = Meson(conanfile) meson.test() self.assertIsNone(conanfile.command)
def test_none(): c = ConfDefinition() c.loads(textwrap.dedent("""\ """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) njobs = ninja_jobs_cmd_line_arg(conanfile) assert njobs is None
def test_sdk_path(): conanfile = ConanFileMock() conf = ConfDefinition() conf.loads("tools.apple:sdk_path=mypath") conanfile.conf = conf conanfile.settings = MockSettings({}) xcodebuild = XcodeBuild(conanfile) xcodebuild.build("app.xcodeproj") assert "SDKROOT=mypath " in conanfile.command
def test_tools_ning(): c = ConfDefinition() c.loads(textwrap.dedent("""\ tools.microsoft.msbuild:max_cpu_count=23 """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) max_cpu_count = msbuild_max_cpu_count_cmd_line_arg(conanfile) assert max_cpu_count == "/m:23"
def test_tools_build(): c = ConfDefinition() c.loads(textwrap.dedent("""\ tools.build:processes=10 """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) max_cpu_count = msbuild_max_cpu_count_cmd_line_arg(conanfile) assert max_cpu_count == "/m:10"
def test_tools_build(): c = ConfDefinition() c.loads(textwrap.dedent("""\ tools.build:processes=10 """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) njobs = ninja_jobs_cmd_line_arg(conanfile) assert njobs == "-j10"
def test_conf_list_operations_fails_with_wrong_types(text1, text2): c1 = ConfDefinition() c1.loads(text1) c1_value_type = type(c1.get("user.company.list:objs")).__name__ c2 = ConfDefinition() c2.loads(text2) with pytest.raises(ConanException) as exc_info: c1.update_conf_definition(c2) assert "It's not possible to compose list values and %s ones" % c1_value_type \ in str(exc_info.value)
def test_tools_ning(): c = ConfDefinition() c.loads(textwrap.dedent("""\ tools.ninja:jobs=23 """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) njobs = ninja_jobs_cmd_line_arg(conanfile) assert njobs == "-j23"
def test_conf_error_uppercase(): text = "tools.something:Verbosity=minimal" c = ConfDefinition() with pytest.raises(ConanException, match=r"Conf key 'Verbosity' must be lowercase"): c.loads(text) text = "tools.Something:verbosity=minimal" c = ConfDefinition() with pytest.raises( ConanException, match=r"Conf module 'tools.Something' must be lowercase"): c.loads(text)
def conanfile(): c = ConfDefinition() c.loads( textwrap.dedent("""\ tools.gnu.make:jobs=40 tools.ninja:jobs=30 tools.microsoft.msbuild:max_cpu_count=20 tools.build:processes=10 """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) return conanfile
def test_conf_rebase(conf_definition): c, _ = conf_definition text = textwrap.dedent("""\ user.company.toolchain:flags=newvalue another.something:key=value""") c2 = ConfDefinition() c2.loads(text) c.rebase_conf_definition(c2) # The c profile will have precedence, and " result = textwrap.dedent("""\ tools.microsoft.msbuild:verbosity=minimal user.company.toolchain:flags=someflags""") assert c.dumps() == result
def test_conf_update(conf_definition): c, _ = conf_definition text = textwrap.dedent("""\ user.company.toolchain:flags=newvalue another.something:key=value""") c2 = ConfDefinition() c2.loads(text) c.update_conf_definition(c2) result = textwrap.dedent("""\ another.something:key=value tools.microsoft.msbuild:verbosity=minimal user.company.toolchain:flags=newvalue""") assert c.dumps() == result
def test_bazel_command_with_empty_config(): c = ConfDefinition() c.loads(textwrap.dedent("""\ tools.google.bazel:config= tools.google.bazel:bazelrc_path= """)) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) bazel = Bazel(conanfile) bazel.build(label='//test:label') assert 'bazel build //test:label' == str(conanfile.command)
def test_verbosity(mode): conanfile = ConanFileMock() conf = ConfDefinition() conf.loads("tools.apple.xcodebuild:verbosity={}".format(mode)) conanfile.conf = conf conanfile.settings = MockSettings({}) xcodebuild = XcodeBuild(conanfile) if mode != "invalid": xcodebuild.build("app.xcodeproj") assert "-{}".format(mode) in conanfile.command else: with pytest.raises(ConanException) as excinfo: xcodebuild.build("app.xcodeproj") assert "Value {} for 'tools.apple.xcodebuild:verbosity' is not valid".format( mode) == str(excinfo.value)
def test_unix_path(subsystem, expected_path): c = ConfDefinition() c.loads( textwrap.dedent("""\ tools.microsoft.bash:subsystem={} """.format(subsystem))) settings = MockSettings({"os": "Windows"}) conanfile = ConanFileMock() conanfile.conf = c.get_conanfile_conf(None) conanfile.settings = settings conanfile.settings_build = settings conanfile.win_bash = True path = unix_path(conanfile, "c:/path/to/stuff") assert expected_path == path
def test_compose_conf_complex(): """ Testing the composition between several ConfDefiniton objects and with different value types """ text = textwrap.dedent("""\ user.company.cpu:jobs=10 user.company.build:ccflags=--m superflag zlib:user.company.check:shared=True zlib:user.company.check:shared_str="True" user.company.list:objs=[1, 2, 3, 4, 'mystr', {'a': 1}] user.company.network:proxies={'url': 'http://api.site.com/api', 'dataType': 'json', 'method': 'GET'} """) c = ConfDefinition() c.loads(text) text = textwrap.dedent("""\ user.company.cpu:jobs=5 user.company.build:ccflags=--m otherflag zlib:user.company.check:shared=! zlib:user.company.check:shared_str="False" user.company.list:objs+=[5, 6] user.company.list:objs=+0 user.company.list:objs+={'b': 2} user.company.network:proxies={'url': 'http://api.site.com/apiv2'} """) c2 = ConfDefinition() c2.loads(text) c.update_conf_definition(c2) expected_text = textwrap.dedent("""\ user.company.cpu:jobs=5 user.company.build:ccflags=--m otherflag user.company.list:objs=[0, 1, 2, 3, 4, 'mystr', {'a': 1}, 5, 6, {'b': 2}] user.company.network:proxies={'url': 'http://api.site.com/apiv2'} zlib:user.company.check:shared=! zlib:user.company.check:shared_str="False" """) if sys.version_info.major == 2: # problems with the order in Python 2.x text = c.dumps() assert all([line in text for line in expected_text.splitlines()]) else: assert c.dumps() == expected_text
def test_conf_pop(): text = textwrap.dedent("""\ user.company.cpu:jobs=5 user.company.build:ccflags=--m otherflag user.company.list:objs=[0, 1, 2, 3, 4, 'mystr', {'a': 1}, 5, 6] user.company.network:proxies={'url': 'http://api.site.com/apiv2', 'dataType': 'json', 'method': 'GET'} zlib:user.company.check:shared=! zlib:user.company.check:shared_str="False" """) c = ConfDefinition() c.loads(text) assert c.pop("user.company.network:proxies") == { 'url': 'http://api.site.com/apiv2', 'dataType': 'json', 'method': 'GET' } assert c.pop("tools.microsoft.msbuild:missing") is None assert c.pop("tools.microsoft.msbuild:missing", default="fake") == "fake" assert c.pop("zlib:user.company.check:shared_str") == '"False"'
def test_meson_build(): c = ConfDefinition() c.loads(textwrap.dedent("""\ tools.ninja:jobs=23 tools.build:processes=10 """)) settings = MockSettings({"build_type": "Release", "compiler": "gcc", "compiler.version": "7", "os": "Linux", "arch": "x86_64"}) conanfile = ConanFileMock() conanfile.settings = settings conanfile.display_name = 'test' conanfile.conf = c.get_conanfile_conf(None) meson = Meson(conanfile) meson.build() assert '-j23' in str(conanfile.command)
def test_msbuild_cpu_count(): c = ConfDefinition() c.loads( textwrap.dedent("""\ tools.microsoft.msbuild:max_cpu_count=23 """)) settings = MockSettings({ "build_type": "Release", "compiler": "gcc", "compiler.version": "7", "os": "Linux", "arch": "x86_64" }) conanfile = ConanFileMock() conanfile.settings = settings conanfile.conf = c.get_conanfile_conf(None) msbuild = MSBuild(conanfile) cmd = msbuild.command('project.sln') assert '/m:23' in cmd
class ClientCache(object): """ Class to represent/store/compute all the paths involved in the execution of conans commands. Accesses to real disk and reads/write things. (OLD client ConanPaths) """ def __init__(self, cache_folder, output): self.cache_folder = cache_folder self._output = output # Caching self._no_lock = None self._config = None self._new_config = None self.editable_packages = EditablePackages(self.cache_folder) # paths self._store_folder = self.config.storage_path or os.path.join( self.cache_folder, "data") # Just call it to make it raise in case of short_paths misconfiguration _ = self.config.short_paths_home def all_refs(self): subdirs = list_folder_subdirs(basedir=self._store_folder, level=4) return [ConanFileReference.load_dir_repr(folder) for folder in subdirs] @property def store(self): return self._store_folder def installed_as_editable(self, ref): return isinstance(self.package_layout(ref), PackageEditableLayout) @property def config_install_file(self): return os.path.join(self.cache_folder, "config_install.json") def package_layout(self, ref, short_paths=None): assert isinstance(ref, ConanFileReference), "It is a {}".format(type(ref)) edited_ref = self.editable_packages.get(ref.copy_clear_rev()) if edited_ref: conanfile_path = edited_ref["path"] layout_file = edited_ref["layout"] return PackageEditableLayout(os.path.dirname(conanfile_path), layout_file, ref, conanfile_path) else: _check_ref_case(ref, self.store) base_folder = os.path.normpath( os.path.join(self.store, ref.dir_repr())) return PackageCacheLayout(base_folder=base_folder, ref=ref, short_paths=short_paths, no_lock=self._no_locks()) @property def remotes_path(self): return os.path.join(self.cache_folder, REMOTES) @property def registry(self): return RemoteRegistry(self, self._output) def _no_locks(self): if self._no_lock is None: self._no_lock = self.config.cache_no_locks return self._no_lock @property def artifacts_properties_path(self): return os.path.join(self.cache_folder, ARTIFACTS_PROPERTIES_FILE) def read_artifacts_properties(self): ret = {} if not os.path.exists(self.artifacts_properties_path): save(self.artifacts_properties_path, "") return ret try: contents = load(self.artifacts_properties_path) for line in contents.splitlines(): if line and not line.strip().startswith("#"): tmp = line.split("=", 1) if len(tmp) != 2: raise Exception() name = tmp[0].strip() value = tmp[1].strip() ret[str(name)] = str(value) return ret except Exception: raise ConanException("Invalid %s file!" % self.artifacts_properties_path) @property def config(self): if not self._config: self.initialize_config() self._config = ConanClientConfigParser(self.conan_conf_path) return self._config @property def new_config_path(self): return os.path.join(self.cache_folder, "conan.cfg") @property def new_config(self): """ this is the new conan.cfgto replace the old conan.conf that contains configuration defined with the new syntax as in profiles, this config will be composed to the profile ones and passed to the conanfiles.conf, which can be passed to collaborators """ if self._new_config is None: self._new_config = ConfDefinition() if os.path.exists(self.new_config_path): self._new_config.loads(load(self.new_config_path)) return self._new_config @property def localdb(self): localdb_filename = os.path.join(self.cache_folder, LOCALDB) encryption_key = os.getenv('CONAN_LOGIN_ENCRYPTION_KEY', None) return LocalDB.create(localdb_filename, encryption_key=encryption_key) @property def conan_conf_path(self): return os.path.join(self.cache_folder, CONAN_CONF) @property def profiles_path(self): return os.path.join(self.cache_folder, PROFILES_FOLDER) @property def settings_path(self): return os.path.join(self.cache_folder, CONAN_SETTINGS) @property def generators_path(self): return os.path.join(self.cache_folder, GENERATORS_FOLDER) @property def default_profile_path(self): if os.path.isabs(self.config.default_profile): return self.config.default_profile else: return os.path.join(self.cache_folder, PROFILES_FOLDER, self.config.default_profile) @property def hooks_path(self): """ :return: Hooks folder in client cache """ return os.path.join(self.cache_folder, HOOKS_FOLDER) @property def default_profile(self): self.initialize_default_profile() default_profile, _ = read_profile(self.default_profile_path, get_cwd(), self.profiles_path) # Mix profile settings with environment mixed_settings = _mix_settings_with_env(default_profile.settings) default_profile.settings = mixed_settings return default_profile @property def settings(self): """Returns {setting: [value, ...]} defining all the possible settings without values""" self.initialize_settings() content = load(self.settings_path) return Settings.loads(content) @property def hooks(self): """Returns a list of hooks inside the hooks folder""" hooks = [] for hook_name in os.listdir(self.hooks_path): if os.path.isfile(hook_name) and hook_name.endswith(".py"): hooks.append(hook_name[:-3]) return hooks @property def generators(self): """Returns a list of generator paths inside the generators folder""" generators = [] if os.path.exists(self.generators_path): for path in os.listdir(self.generators_path): generator = os.path.join(self.generators_path, path) if os.path.isfile(generator) and generator.endswith(".py"): generators.append(generator) return generators def delete_empty_dirs(self, deleted_refs): """ Method called by ConanRemover.remove() to clean up from the cache empty folders :param deleted_refs: The recipe references that the remove() has been removed """ for ref in deleted_refs: ref_path = self.package_layout(ref).base_folder() for _ in range(4): if os.path.exists(ref_path): try: # Take advantage that os.rmdir does not delete non-empty dirs os.rmdir(ref_path) except OSError: break # not empty ref_path = os.path.dirname(ref_path) def remove_locks(self): folders = list_folder_subdirs(self._store_folder, 4) for folder in folders: conan_folder = os.path.join(self._store_folder, folder) Lock.clean(conan_folder) shutil.rmtree(os.path.join(conan_folder, "locks"), ignore_errors=True) def get_template(self, template_name, user_overrides=False): # TODO: It can be initialized only once together with the Conan app loaders = [dict_loader] if user_overrides: loaders.insert( 0, FileSystemLoader(os.path.join(self.cache_folder, 'templates'))) env = Environment(loader=ChoiceLoader(loaders), autoescape=select_autoescape(['html', 'xml'])) return env.get_template(template_name) def initialize_config(self): if not os.path.exists(self.conan_conf_path): save(self.conan_conf_path, normalize(get_default_client_conf())) def reset_config(self): if os.path.exists(self.conan_conf_path): remove(self.conan_conf_path) self.initialize_config() def initialize_default_profile(self): if not os.path.exists(self.default_profile_path): self._output.writeln( "Auto detecting your dev setup to initialize the " "default profile (%s)" % self.default_profile_path, Color.BRIGHT_YELLOW) default_settings = detect_defaults_settings( self._output, profile_path=self.default_profile_path) self._output.writeln("Default settings", Color.BRIGHT_YELLOW) self._output.writeln( "\n".join(["\t%s=%s" % (k, v) for (k, v) in default_settings]), Color.BRIGHT_YELLOW) self._output.writeln( "*** You can change them in %s ***" % self.default_profile_path, Color.BRIGHT_MAGENTA) self._output.writeln( "*** Or override with -s compiler='other' -s ...s***\n\n", Color.BRIGHT_MAGENTA) default_profile = Profile() tmp = OrderedDict(default_settings) default_profile.update_settings(tmp) save(self.default_profile_path, default_profile.dumps()) def reset_default_profile(self): if os.path.exists(self.default_profile_path): remove(self.default_profile_path) self.initialize_default_profile() def initialize_settings(self): if not os.path.exists(self.settings_path): save(self.settings_path, normalize(get_default_settings_yml())) def reset_settings(self): if os.path.exists(self.settings_path): remove(self.settings_path) self.initialize_settings()
def test_parse_spaces(): text = "core:verbosity = minimal" c = ConfDefinition() c.loads(text) assert c["core:verbosity"] == "minimal"