def create_local_svn_checkout(files, repo_url, rel_project_path=None, commit_msg='default commit message', delete_checkout=True, folder=None): tmp_dir = folder or temp_folder() try: rel_project_path = rel_project_path or str(uuid.uuid4()) # Do not use SVN class as it is what we will be testing subprocess.check_output('svn co "{url}" "{path}"'.format(url=repo_url, path=tmp_dir), shell=True) tmp_project_dir = os.path.join(tmp_dir, rel_project_path) mkdir(tmp_project_dir) save_files(tmp_project_dir, files) with chdir(tmp_project_dir): subprocess.check_output("svn add .", shell=True) subprocess.check_output('svn commit -m "{}"'.format(commit_msg), shell=True) if SVN.get_version() >= SVN.API_CHANGE_VERSION: rev = check_output("svn info --show-item revision").strip() else: import xml.etree.ElementTree as ET output = check_output("svn info --xml").strip() root = ET.fromstring(output) rev = root.findall("./entry")[0].get("revision") project_url = repo_url + "/" + quote( rel_project_path.replace("\\", "/")) return project_url, rev finally: if delete_checkout: shutil.rmtree(tmp_dir, ignore_errors=False, onerror=try_remove_readonly)
def environment_deactivate_test(self): if platform.system() == "Windows": """ This test fails. The deactivation script takes the value of some envvars set by the activation script to recover the previous values (set PATH=OLD_PATH). As this test is running each command in a different shell, the envvar OLD_PATH that has been set by the 'activate' script doesn't exist when we run 'deactivate' in a different shell... TODO: Remove this test """ self.skipTest("This won't work in Windows") in_windows = platform.system() == "Windows" env_cmd = "set" if in_windows else "env" extension = "bat" if in_windows else "sh" def env_output_to_dict(env_output): env = {} for line in env_output.splitlines(): tmp = line.split("=") # OLDPWD is cleared when a child script is started if tmp[0] not in ["SHLVL", "_", "PS1", "OLDPWD"]: env[tmp[0]] = tmp[1].replace("\\", "/") return env def get_cmd(script_name): if in_windows: return "%s && set" % script_name else: return "bash -c 'source %s && env'" % script_name conanfile = textwrap.dedent(""" from conans import ConanFile class TestConan(ConanFile): settings = "os", "compiler", "arch", "build_type" generators = "virtualbuildenv" """) client = TestClient(path_with_spaces=False) client.save({"conanfile.py": conanfile}) client.run("install .") output = check_output(env_cmd) normal_environment = env_output_to_dict(output) client.run("install .") act_build_file = os.path.join(client.current_folder, "activate_build.%s" % extension) deact_build_file = os.path.join(client.current_folder, "deactivate_build.%s" % extension) self.assertTrue(os.path.exists(act_build_file)) self.assertTrue(os.path.exists(deact_build_file)) output = check_output(get_cmd(act_build_file)) activate_environment = env_output_to_dict(output) self.assertNotEqual(normal_environment, activate_environment) output = check_output(get_cmd(deact_build_file)) deactivate_environment = env_output_to_dict(output) self.assertDictEqual(normal_environment, deactivate_environment)
def environment_deactivate_test(self): in_windows = platform.system() == "Windows" env_cmd = "set" if in_windows else "env" extension = "bat" if in_windows else "sh" def env_output_to_dict(env_output): env = {} for line in env_output.splitlines(): tmp = line.split("=") # OLDPWD is cleared when a child script is started if tmp[0] not in ["SHLVL", "_", "PS1", "OLDPWD"]: env[tmp[0]] = tmp[1].replace("\\", "/") return env def get_cmd(script_name): if in_windows: return "%s && set" % script_name else: return "bash -c 'source %s && env'" % script_name conanfile = """ from conans import ConanFile class TestConan(ConanFile): name = "test" version = "1.0" settings = "os", "compiler", "arch", "build_type" generators = "virtualbuildenv" """ client = TestClient(path_with_spaces=False) client.save({"conanfile.py": conanfile}) client.run("install .") output = check_output(env_cmd) normal_environment = env_output_to_dict(output) client.run("install .") act_build_file = os.path.join(client.current_folder, "activate_build.%s" % extension) deact_build_file = os.path.join(client.current_folder, "deactivate_build.%s" % extension) self.assertTrue(os.path.exists(act_build_file)) self.assertTrue(os.path.exists(deact_build_file)) if in_windows: act_build_content_len = len(load(act_build_file).splitlines()) deact_build_content_len = len(load(deact_build_file).splitlines()) self.assertEqual(act_build_content_len, deact_build_content_len) output = check_output(get_cmd(act_build_file)) activate_environment = env_output_to_dict(output) self.assertNotEqual(normal_environment, activate_environment) output = check_output(get_cmd(deact_build_file)) deactivate_environment = env_output_to_dict(output) self.assertEqual(normal_environment, deactivate_environment)
def detect_default_in_mac_os_using_gcc_as_default_test(self): """ Test if gcc in Mac OS X is using apple-clang as frontend """ # See: https://github.com/conan-io/conan/issues/2231 try: output = check_output(["gcc", "--version"], stderr=subprocess.STDOUT) except subprocess.CalledProcessError: # gcc is not installed or there is any error (no test scenario) return if "clang" not in output: # Not test scenario gcc should display clang in output # see: https://stackoverflow.com/questions/19535422/os-x-10-9-gcc-links-to-clang raise Exception( "Apple gcc doesn't point to clang with gcc frontend anymore!") output = TestBufferConanOutput() with tools.environment_append({"CC": "gcc"}): result = detect_defaults_settings( output, profile_path=DEFAULT_PROFILE_NAME) # result is a list of tuples (name, value) so converting it to dict result = dict(result) # No compiler should be detected self.assertIsNone(result.get("compiler", None)) self.assertIn("gcc detected as a frontend using apple-clang", output) self.assertIsNotNone(output.error)
def unix_to_dos_unit_test(self): def save_file(contents): tmp = temp_folder() filepath = os.path.join(tmp, "a_file.txt") save(filepath, contents) return filepath fp = save_file(b"a line\notherline\n") if platform.system() != "Windows": output = check_output(["file", fp], stderr=subprocess.STDOUT) self.assertIn("ASCII text", str(output)) self.assertNotIn("CRLF", str(output)) tools.unix2dos(fp) output = check_output(["file", fp], stderr=subprocess.STDOUT) self.assertIn("ASCII text", str(output)) self.assertIn("CRLF", str(output)) else: fc = tools.load(fp) self.assertNotIn("\r\n", fc) tools.unix2dos(fp) fc = tools.load(fp) self.assertIn("\r\n", fc) self.assertEqual("a line\r\notherline\r\n", str(tools.load(fp))) fp = save_file(b"a line\r\notherline\r\n") if platform.system() != "Windows": output = check_output(["file", fp], stderr=subprocess.STDOUT) self.assertIn("ASCII text", str(output)) self.assertIn("CRLF", str(output)) tools.dos2unix(fp) output = check_output(["file", fp], stderr=subprocess.STDOUT) self.assertIn("ASCII text", str(output)) self.assertNotIn("CRLF", str(output)) else: fc = tools.load(fp) self.assertIn("\r\n", fc) tools.dos2unix(fp) fc = tools.load(fp) self.assertNotIn("\r\n", fc) self.assertEqual("a line\notherline\n", str(tools.load(fp)))
def test_short_paths_home_set_acl(self): """ When CONAN_USER_HOME_SHORT is living in NTFS file systems, current user needs to be granted with full control permission to avoid access problems when cygwin/msys2 windows subsystems are mounting/using that folder. """ cache_folder = temp_folder( False) # Creates a temporary folder in %HOME%\appdata\local\temp out = subprocess.check_output("wmic logicaldisk %s get FileSystem" % os.path.splitdrive(cache_folder)[0]) if "NTFS" not in str(out): return short_folder = os.path.join(temp_folder(False), ".cnacls") self.assertFalse(os.path.exists(short_folder), "short_folder: %s shouldn't exists" % short_folder) os.makedirs(short_folder) current_domain = os.environ['USERDOMAIN'] current_user = os.environ['USERNAME'] # Explicitly revoke full control permission to current user cmd = r'cacls %s /E /R "%s\%s"' % (short_folder, current_domain, current_user) try: subprocess.check_output(cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: raise Exception("Error %s setting ACL to short_folder: '%s'." "Please check that cacls.exe exists" % (e, short_folder)) # Run conan export in using short_folder with tools.environment_append({"CONAN_USER_HOME_SHORT": short_folder}): client = TestClient(cache_folder=cache_folder) client.save({CONANFILE: conanfile_py.replace("False", "True")}) client.run("export . %s" % self.user_channel) # Retrieve ACLs from short_folder try: short_folder_acls = check_output("cacls %s" % short_folder, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: raise Exception("Error %s getting ACL from short_folder: '%s'." % (e, short_folder)) # Check user has full control user_acl = "%s\\%s:(OI)(CI)F" % (current_domain, current_user) self.assertIn(user_acl, short_folder_acls)
def vcvars_dict(settings, arch=None, compiler_version=None, force=False, filter_known_paths=False, vcvars_ver=None, winsdk_version=None, only_diff=True, output=None): known_path_lists = ("include", "lib", "libpath", "path") cmd = vcvars_command(settings, arch=arch, compiler_version=compiler_version, force=force, vcvars_ver=vcvars_ver, winsdk_version=winsdk_version, output=output) cmd += " && set" ret = check_output(cmd) new_env = {} for line in ret.splitlines(): line = line.strip() if line == "\n" or not line: continue try: name_var, value = line.split("=", 1) new_value = value.split( os.pathsep) if name_var.lower() in known_path_lists else value # Return only new vars & changed ones, but only with the changed elements if the var is # a list if only_diff: old_value = os.environ.get(name_var) if name_var.lower() == "path": old_values_lower = [ v.lower() for v in old_value.split(os.pathsep) ] # Clean all repeated entries, not append if the element was already there new_env[name_var] = [ v for v in new_value if v.lower() not in old_values_lower ] elif old_value and value.endswith(os.pathsep + old_value): # The new value ends with separator and the old value, is a list, # get only the new elements new_env[name_var] = value[:-(len(old_value) + 1)].split( os.pathsep) elif value != old_value: # Only if the vcvars changed something, we return the variable, # otherwise is not vcvars related new_env[name_var] = new_value else: new_env[name_var] = new_value except ValueError: pass if filter_known_paths: def relevant_path(path): path = path.replace("\\", "/").lower() keywords = "msbuild", "visual", "microsoft", "/msvc/", "/vc/", "system32", "windows" return any(word in path for word in keywords) path_key = next( (name for name in new_env.keys() if "path" == name.lower()), None) if path_key: path = [ entry for entry in new_env.get(path_key, "") if relevant_path(entry) ] new_env[path_key] = ";".join(path) return new_env
def vswhere(all_=False, prerelease=False, products=None, requires=None, version="", latest=False, legacy=False, property_="", nologo=True): # 'version' option only works if Visual Studio 2017 is installed: # https://github.com/Microsoft/vswhere/issues/91 products = list() if products is None else products requires = list() if requires is None else requires if legacy and (products or requires): raise ConanException( "The 'legacy' parameter cannot be specified with either the " "'products' or 'requires' parameter") installer_path = None program_files = get_env("ProgramFiles(x86)") or get_env("ProgramFiles") if program_files: expected_path = os.path.join(program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe") if os.path.isfile(expected_path): installer_path = expected_path vswhere_path = installer_path or which("vswhere") if not vswhere_path: raise ConanException( "Cannot locate vswhere in 'Program Files'/'Program Files (x86)' " "directory nor in PATH") arguments = list() arguments.append(vswhere_path) # Output json format arguments.append("-format") arguments.append("json") if all_: arguments.append("-all") if prerelease: arguments.append("-prerelease") if products: arguments.append("-products") arguments.extend(products) if requires: arguments.append("-requires") arguments.extend(requires) if len(version) is not 0: arguments.append("-version") arguments.append(version) if latest: arguments.append("-latest") if legacy: arguments.append("-legacy") if len(property_) is not 0: arguments.append("-property") arguments.append(property_) if nologo: arguments.append("-nologo") try: output = check_output(arguments).strip() # Ignore the "description" field, that even decoded contains non valid charsets for json # (ignored ones) output = "\n".join([ line for line in output.splitlines() if not line.strip().startswith('"description"') ]) except (ValueError, subprocess.CalledProcessError, UnicodeDecodeError) as e: raise ConanException("vswhere error: %s" % str(e)) return json.loads(output)
def test_shared_run_environment(self): servers = {"default": TestServer()} client = TestClient(servers=servers, users={"default": [("lasote", "mypass")]}) cmake = """set(CMAKE_CXX_COMPILER_WORKS 1) set(CMAKE_CXX_ABI_COMPILED 1) project(MyHello CXX) cmake_minimum_required(VERSION 2.8.12) add_library(hello SHARED hello.cpp) add_executable(say_hello main.cpp) target_link_libraries(say_hello hello)""" hello_h = """#ifdef WIN32 #define HELLO_EXPORT __declspec(dllexport) #else #define HELLO_EXPORT #endif HELLO_EXPORT void hello(); """ hello_cpp = r"""#include "hello.h" #include <iostream> void hello(){ std::cout<<"Hello Tool!\n"; } """ main = """#include "hello.h" int main(){ hello(); } """ conanfile = """from conans import ConanFile, CMake class Pkg(ConanFile): exports_sources = "*" def build(self): cmake = CMake(self) cmake.configure() cmake.build() def package(self): self.copy("*say_hello.exe", dst="bin", keep_path=False) self.copy("*say_hello", dst="bin", keep_path=False) self.copy(pattern="*.dll", dst="bin", keep_path=False) self.copy(pattern="*.dylib", dst="lib", keep_path=False) self.copy(pattern="*.so", dst="lib", keep_path=False) """ client.save({ "conanfile.py": conanfile, "CMakeLists.txt": cmake, "main.cpp": main, "hello.cpp": hello_cpp, "hello.h": hello_h }) client.run("create . Pkg/0.1@lasote/testing") client.run("upload Pkg* --all --confirm") client.run('remove "*" -f') client.run("search") self.assertIn("There are no packages", client.out) # MAKE SURE WE USE ANOTHER CLIENT, with another USER HOME PATH client2 = TestClient(servers=servers, users={"default": [("lasote", "mypass")]}) self.assertNotEqual(client2.base_folder, client.base_folder) reuse = '''from conans import ConanFile class HelloConan(ConanFile): requires = "Pkg/0.1@lasote/testing" def build(self): self.run("say_hello", run_environment=True) ''' client2.save({"conanfile.py": reuse}, clean_first=True) client2.run("install .") client2.run("build .") self.assertIn("Hello Tool!", client2.out) if platform.system() != "Darwin": # This test is excluded from OSX, because of the SIP protection. CMake helper will # launch a subprocess with shell=True, which CLEANS the DYLD_LIBRARY_PATH. Injecting its # value via run_environment=True doesn't work, because it prepends its value to: # command = "cd [folder] && cmake [cmd]" => "DYLD_LIBRARY_PATH=[path] cd [folder] && cmake [cmd]" # and then only applies to the change directory "cd" # If CMake binary is in user folder, it is not under SIP, and it can work. For cmake installed in # system folders, then no possible form of "DYLD_LIBRARY_PATH=[folders] cmake" can work reuse = '''from conans import ConanFile, CMake, tools class HelloConan(ConanFile): exports = "CMakeLists.txt" requires = "Pkg/0.1@lasote/testing" def build(self): with tools.run_environment(self): cmake = CMake(self) cmake.configure() cmake.build() ''' cmake = """set(CMAKE_CXX_COMPILER_WORKS 1) set(CMAKE_CXX_ABI_COMPILED 1) project(MyHello CXX) cmake_minimum_required(VERSION 2.8.12) execute_process(COMMAND say_hello) """ client2.save({ "conanfile.py": reuse, "CMakeLists.txt": cmake }, clean_first=True) client2.run("install . -g virtualrunenv") client2.run("build .") self.assertIn("Hello Tool!", client2.out) else: client2.run("install . -g virtualrunenv") with tools.chdir(client2.current_folder): if platform.system() == "Windows": command = "activate_run.bat && say_hello" else: # It is not necessary to use the DYLD_LIBRARY_PATH in OSX because the activate_run.sh # will work perfectly. It is inside the bash, so the loader will use DYLD_LIBRARY_PATH # values. It also works in command line with export DYLD_LIBRARY_PATH=[path] and then # running, or in the same line "$ DYLD_LIBRARY_PATH=[path] say_hello" command = "bash -c 'source activate_run.sh && say_hello'" output = check_output(command) self.assertIn("Hello Tool!", output)
def _cmd_output(command): return check_output(command).strip()
def cmd_output(cmd): return check_output(cmd).strip()