def test_cmake_toolchain__without_toolchain(self): config = BuildConfig() cmake_toolchain1 = config.get("cmake_toolchain", None) cmake_toolchain2 = config["cmake_toolchain"] assert config.cmake_toolchain is None assert cmake_toolchain1 is None assert cmake_toolchain2 is None
def test_cmake_generator__without_generator(self): config = BuildConfig() cmake_generator1 = config.get("cmake_generator", None) cmake_generator2 = config["cmake_generator"] assert config.cmake_generator is None assert cmake_generator1 is None assert cmake_generator2 is None
def test_cmake_defines_add__with_existing_item_removes_it(self): definitions = [("one", "VALUE_1"), ("two", "VALUE_2")] config = BuildConfig(cmake_defines=definitions) config.cmake_defines_remove("two") expected = [("one", "VALUE_1"), ("CMAKE_BUILD_TYPE", None)] assert list(config.cmake_defines.items()) == expected assert list(config["cmake_defines"].items()) == expected
def test_cmake_defines__set(self): config = BuildConfig() definitions = [("one", "VALUE_1"), ("two", "VALUE_2")] config.cmake_defines = definitions # XXX-BAD # XXX expected = [("CMAKE_BUILD_TYPE", None)] + definitions assert list(config.cmake_defines.items()) == definitions assert list(config["cmake_defines"].items()) == definitions
def test_cmake_defines__with_one_item(self): config = BuildConfig(cmake_defines=[("one", "VALUE_1")]) expected = [("one", "VALUE_1"), ("CMAKE_BUILD_TYPE", None)] assert isinstance(config.cmake_defines, OrderedDict) assert list(config.cmake_defines.items()) == expected assert list(config.get("cmake_defines").items()) == expected assert list(config["cmake_defines"].items()) == expected assert list(config.data.get("cmake_defines").items()) == expected
def test_derive_cmake_build_type_and_assign(self, build_config_name, expected): config = BuildConfig(build_config_name, cmake_build_type="INITIAL") assert config.name == build_config_name assert config.cmake_build_type == "INITIAL" cmake_build_type = config.derive_cmake_build_type_and_assign() assert cmake_build_type == expected assert config.cmake_build_type == expected
def test_derive_cmake_build_type_if_unconfigured(self, build_config_name, expected): config = BuildConfig(build_config_name) config.cmake_build_type = None # -- DISABLED: default-init. assert config.name == build_config_name assert config.cmake_build_type is None, "ENSURE: No assignment" cmake_build_type = config.derive_cmake_build_type_if_unconfigured() assert cmake_build_type == expected assert config.cmake_build_type == expected
def test_cmake_defines_add__with_new_item_appends(self): config = BuildConfig(cmake_defines=[("one", "VALUE_1")]) config.cmake_defines_add("NEW", "NEW_VALUE") expected = [ ("one", "VALUE_1"), ("CMAKE_BUILD_TYPE", None), ("NEW", "NEW_VALUE"), ] assert list(config.cmake_defines.items()) == expected assert list(config["cmake_defines"].items()) == expected
def test_derive_cmake_build_type_if_unconfigured__with_initial_value( self, build_config_name, expected): config = BuildConfig(build_config_name, cmake_build_type="INITIAL") assert config.name == build_config_name assert config.cmake_build_type == "INITIAL" config.derive_cmake_build_type_if_unconfigured() derived_build_type = config.derive_cmake_build_type() assert config.cmake_build_type == "INITIAL", "ENSURE: INITIAL value is preserved." assert derived_build_type == expected assert derived_build_type != "INITIAL"
def test_build__auto_init_with_nonexisting_build_dir(self, tmpdir, capsys): ctx = MockContext() project_dir = Path(str(tmpdir)) project_build_dir = project_dir/"build" build_config = BuildConfig(cmake_generator="ninja") cmake_project1 = CMakeProject(ctx, project_dir, project_build_dir, build_config) assert not project_build_dir.isdir() # -- STEP: First cmake_project.build => AUTO CMAKE-INIT: project_build_dir with cd(project_dir): assert not project_build_dir.exists() cmake_project1.build() # -- POSTCONDITIONS: expected_commands = [ "cmake -G Ninja ..", "cmake --build .", ] assert ctx.commands == expected_commands cmake_build_filename = project_build_dir / CMakeProjectPersistConfig.FILE_BASENAME captured = capsys.readouterr() assert_cmake_project_used_init_using_captured(cmake_project1, captured, cmake_generator="ninja") # assert "CMAKE-INIT: build (using cmake.generator=ninja)" in captured.out assert "CMAKE-BUILD: build" in captured.out assert project_build_dir.exists() assert cmake_build_filename.exists()
def test_init__skip_same_data(self, tmpdir, capsys): ctx = MockContext() project_dir = Path(str(tmpdir)) project_build_dir = project_dir/"build" build_config = BuildConfig(cmake_generator="NINJA") cmake_project1 = CMakeProject(ctx, project_dir, project_build_dir, build_config) assert not project_build_dir.isdir() # -- STEP 1: First cmake_project.init with cd(project_dir): cmake_project1.init() # -- POSTCONDITIONS: cmake_build_filename = project_build_dir / CMakeProjectPersistConfig.FILE_BASENAME assert project_build_dir.exists(), "ENSURE: project_build_dir exists" assert cmake_build_filename.exists() captured = capsys.readouterr() assert_cmake_project_used_init_using_captured(cmake_project1, captured, cmake_generator="NINJA") # -- STEP 2: Second cmake_project.init => SKIPPED with cd(project_dir): ctx.clear() cmake_project2 = CMakeProject(ctx, project_dir.relpath(), project_build_dir.relpath(), build_config) cmake_project2.init() captured = capsys.readouterr() assert ctx.last_command is None assert_cmake_project_skipped_reinit_using_captured(cmake_project1, captured)
def test_init__when_build_dir_exists_with_other_persistent_schema(self, tmpdir, capsys): ctx = MockContext() project_dir = Path(str(tmpdir)) project_build_dir = project_dir/"build" build_config = BuildConfig(cmake_generator="ninja", cmake_build_type="debug") assert not project_build_dir.isdir() # -- STEP 1: First cmake_project.init with cd(project_dir): cmake_project1 = CMakeProject(ctx, project_dir, project_build_dir, build_config) cmake_project1.init() cmake_build_filename = project_build_dir / CMakeProjectPersistConfig.FILE_BASENAME assert project_build_dir.exists(), "ENSURE: project_build_dir exists" assert cmake_build_filename.exists() # -- STEP: Fake cmake-build init with other persistent data schema. # HINT: May occur when cmake-build is updated, but project_build_dir still exists. with open(cmake_build_filename, "w") as f: f.write("""{ "other": 123, "cmake_generator": "ninja" }""") # -- STEP 2: Second try to cmake_project.init() # ENSURE: No failure / AssertionError occurs with cd(project_dir): cmake_project2 = CMakeProject(ctx, project_dir, project_build_dir, build_config) assert cmake_project2.initialized assert not cmake_project2.needs_reinit() assert cmake_project2.needs_update() cmake_project2.init() assert not cmake_project2.needs_reinit() assert not cmake_project2.needs_update() captured = capsys.readouterr() assert_cmake_project_needed_update_using_captured(cmake_project2, captured, cmake_generator="ninja")
def test_init__init_without_build_dir(self, tmpdir): ctx = MockContext() project_dir = Path(str(tmpdir)) project_build_dir = project_dir/"build" build_config = BuildConfig(cmake_generator="NINJA") cmake_project = CMakeProject(ctx, project_dir, project_build_dir, build_config) assert not project_build_dir.isdir() with cd(project_dir): cmake_project.init() assert ctx.last_command == "cmake -G NINJA .."
def test_build__performs_reinit_with_existing_build_dir_and_other_cmake_generator(self, tmpdir, capsys): ctx = MockContext() project_dir = Path(str(tmpdir)) project_build_dir = project_dir/"build" build_config = BuildConfig(cmake_generator="ninja") assert not project_build_dir.isdir() # -- STEP 1: First cmake_project.init with cd(project_dir): cmake_project1 = CMakeProject(ctx, project_dir, project_build_dir, build_config) cmake_project1.init() # -- POSTCONDITIONS: cmake_build_filename = project_build_dir / CMakeProjectPersistConfig.FILE_BASENAME assert project_build_dir.exists(), "ENSURE: project_build_dir exists" assert cmake_build_filename.exists() captured = capsys.readouterr() assert_cmake_project_used_init_using_captured(cmake_project1, captured, cmake_generator="ninja") # assert "CMAKE-INIT: build (using cmake.generator=ninja)" in captured.out assert ctx.last_command == "cmake -G Ninja .." # -- STEP 2: Second cmake_project.build => REINIT. ctx.clear() with cd(project_dir): build_config.cmake_generator = "OTHER" # ONLY-DEFAULT cmake_project2 = CMakeProject(ctx, project_dir, project_build_dir, build_config, cmake_generator="make") assert cmake_project2.needs_reinit() cmake_project2.build() # -- POSTCONDITIONS: expected_commands = [ 'cmake -G "Unix Makefiles" ..', "cmake --build ." ] assert ctx.commands == expected_commands captured = capsys.readouterr() assert_cmake_project_used_reinit_using_captured(cmake_project2, captured, cmake_generator="make")
def test_init__skips_reinit_with_existing_build_dir_and_generator_none(self, tmpdir, capsys): ctx = MockContext() project_dir = Path(str(tmpdir)) project_build_dir = project_dir/"build" build_config = BuildConfig(cmake_generator="ninja") assert not project_build_dir.isdir() # -- STEP 1: First cmake_project.init with cd(project_dir): cmake_project1 = CMakeProject(ctx, project_dir, project_build_dir, build_config) cmake_project1.init() # -- POSTCONDITIONS: cmake_build_filename = project_build_dir / CMakeProjectPersistConfig.FILE_BASENAME assert project_build_dir.exists(), "ENSURE: project_build_dir exists" assert cmake_build_filename.exists() captured = capsys.readouterr() assert_cmake_project_used_init_using_captured(cmake_project1, captured, cmake_generator="ninja") # assert "CMAKE-INIT: build (using cmake.generator=ninja)" in captured.out assert ctx.last_command == "cmake -G Ninja .." # -- STEP 2: Second cmake_project.build => SKIP-REINIT. # REASON: # * Inherit stored.cmake_generator (if it is not overridden). # * Keep stored.cmake_generator until explicit reinit. ctx.clear() with cd(project_dir): build_config.cmake_generator = "OTHER" # -- ONLY DEFAULT-VALUE cmake_project2 = CMakeProject(ctx, project_dir, project_build_dir, build_config, cmake_generator=None) assert not cmake_project2.needs_reinit() cmake_project2.init() # -- POSTCONDITIONS: expected_commands = [] assert ctx.commands == expected_commands captured = capsys.readouterr() assert_cmake_project_skipped_reinit_using_captured(cmake_project2, captured, cmake_generator="ninja")
def make_newborn(tmpdir, cmake_generator=None, cmake_build_type=None): """Create NEW-BORN CMake project without build-directory.""" cmake_generator = cmake_generator or DEFAULT_CMAKE_GENERATOR cmake_build_type = cmake_build_type or DEFAULT_CMAKE_BUILD_TYPE ctx = MockContext() project_dir = Path(str(tmpdir)) project_build_dir = project_dir / "build" build_config = BuildConfig(cmake_generator=cmake_generator, cmake_build_type=cmake_build_type) assert not project_build_dir.isdir() # -- STEP 1: First cmake_project.init with cd(project_dir): cmake_project = CMakeProject(ctx, project_dir, project_build_dir, build_config) return cmake_project
def __init__(self, ctx, project_dir=None, project_build_dir=None, build_config=None, cmake_generator=None): if build_config is None: cmake_build_type = self.CMAKE_BUILD_TYPE_DEFAULT build_config = BuildConfig("default", cmake_build_type=cmake_build_type) project_dir = Path(project_dir or ".") project_dir = project_dir.abspath() if not project_build_dir: build_dir = make_build_dir_from_schema(ctx.config, build_config.name) project_build_dir = project_dir / build_dir config_name = build_config.name cmake_generator_default = build_config.cmake_generator cmake_toolchain = build_config.cmake_toolchain self.ctx = ctx self.project_dir = project_dir self.project_build_dir = Path(project_build_dir).abspath() self.config = None self._build_config = build_config self._stored_config = None self._stored_cmake_generator = None self._dirty = True self._placeholder_map = {} self.load_config() self.update_from_initial_config(build_config) self.config.name = config_name if not cmake_generator: # -- INHERIT: Stored cmake_generator, if it is not overridden. cmake_generator = self._stored_config.cmake_generator or \ cmake_generator_default self.config.cmake_generator = cmake_generator self.config.cmake_toolchain = cmake_toolchain self.cmake_install_prefix = self.replace_placeholders( self.cmake_install_prefix) self.cmake_config_overrides_cmake_build_type = \ self.CMAKE_CONFIG_OVERRIDES_CMAKE_BUILD_TYPE # MAYBE: # self.cmake_defines = self.replace_placeholders(self.cmake_defines) self._dirty = True
def make_build_config(ctx, name=None): if name == "host_debug" or name == "auto": name = HOST_BUILD_CONFIG_DEBUG elif name == "host_release": name = HOST_BUILD_CONFIG_RELEASE name = name or ctx.config.build_config or "default" build_config_defaults = make_build_config_defaults(ctx.config) build_config_data = {} build_config_data.update(build_config_defaults) build_config_data2 = ctx.config.build_configs_map.get(name) or {} build_config_data.update(build_config_data2) # -- STEP: Make cmake_toolchain path relative to config_dir. config_dir = ctx.config.config_dir or "." cmake_toolchain = build_config_data.get("cmake_toolchain") if cmake_toolchain: # -- ASSUMPTION: cmake_toolchain is a relative-path. cmake_toolchain = Path(cmake_toolchain) if not cmake_toolchain.isabs(): cmake_toolchain = Path(config_dir) / cmake_toolchain cmake_toolchain = cmake_toolchain.normpath() build_config_data["cmake_toolchain"] = cmake_toolchain # -- STEP: build_config.cmake_defines inherits common.cmake_defines cmake_defines = build_config_defaults["cmake_defines"] cmake_defines_items = cmake_defines_normalize( build_config_data2.get("cmake_defines", [])) if cmake_defines_items: # -- INHERIT DEFAULT CMAKE_DEFINES (override and/or merge them): use_override = cmake_defines_items[0][0] == "@override" if use_override: cmake_defines = OrderedDict(cmake_defines_items) else: # -- MERGE-AND-OVERRIDE: # New items are added, existing items replaced/overwritten. cmake_defines = cmake_defines.copy() cmake_defines.update(OrderedDict(cmake_defines_items)) build_config_data["cmake_defines"] = cmake_defines return BuildConfig(name, build_config_data)
def test_init__performs_reinit_with_other_cmake_generator(self, tmpdir, capsys): ctx = MockContext() project_dir = Path(str(tmpdir)).abspath() project_build_dir = project_dir/"build" build_config = BuildConfig(cmake_generator="make") cmake_project1 = CMakeProject(ctx, project_dir, project_build_dir, build_config) assert not project_build_dir.isdir() # -- STEP 1: First cmake_project.init with cd(project_dir): cmake_project1.init() # -- POSTCONDITIONS: cmake_build_filename = project_build_dir / CMakeProjectPersistConfig.FILE_BASENAME assert project_build_dir.exists(), "ENSURE: project_build_dir exists" assert cmake_build_filename.exists() captured = capsys.readouterr() assert_cmake_project_used_init_using_captured(cmake_project1, captured, cmake_generator="make") # assert "CMAKE-INIT: build (using cmake.generator=make)" in captured.out assert ctx.last_command == 'cmake -G "Unix Makefiles" ..' # -- STEP 2: Second cmake_project.init => REINIT: Other cmake_generator is used. with cd(project_dir): ctx.clear() # build_config.cmake_generator = "ninja" cmake_project2 = CMakeProject(ctx, project_dir.relpath(), project_build_dir.relpath(), build_config, cmake_generator="ninja") assert cmake_project2.needs_reinit(), "ENSURE: Need for reinit" cmake_project2.init() captured = capsys.readouterr() assert_cmake_project_used_reinit_using_captured(cmake_project2, captured, cmake_generator="ninja") # assert "CMAKE-INIT: build (NEEDS-REINIT)" in captured.out # assert "CMAKE-INIT: build (using cmake.generator=ninja)" in captured.out assert ctx.last_command == "cmake -G Ninja .."
def test_cmake_toolchain__with_toolchain(self): config = BuildConfig(cmake_toolchain="cmake/toolchain_1.cmake") assert config.cmake_toolchain == "cmake/toolchain_1.cmake" assert config.get("cmake_toolchain") == "cmake/toolchain_1.cmake" assert config["cmake_toolchain"] == "cmake/toolchain_1.cmake"
def test_cmake_generator__with_generator(self): config = BuildConfig(cmake_generator="foo") assert config.cmake_generator == "foo" assert config.get("cmake_generator") == "foo" assert config["cmake_generator"] == "foo" assert config.data.get("cmake_generator") == "foo"
def test_derive_cmake_build_type__returns_none_without_name(self): config = BuildConfig() cmake_build_type = config.derive_cmake_build_type() assert config.name == "default" assert cmake_build_type is None
def test_derive_cmake_build_type(self, build_config_name, expected): config = BuildConfig(build_config_name) cmake_build_type = config.derive_cmake_build_type() assert config.name == build_config_name assert cmake_build_type == expected
def test_cmake_defines__without_explicit_init(self): config = BuildConfig("debug") expected = [("CMAKE_BUILD_TYPE", "Debug")] assert list(config.cmake_defines.items()) == expected assert list(config["cmake_defines"].items()) == expected
def test_cmake_defines_remove__with_unknown_item(self): config = BuildConfig(cmake_defines=[("one", "VALUE_1")]) config.cmake_defines_remove("UNKNOWN") expected = [("one", "VALUE_1"), ("CMAKE_BUILD_TYPE", None)] assert list(config.cmake_defines.items()) == expected assert list(config["cmake_defines"].items()) == expected