Index: lnt/trunk/docs/tests.rst =================================================================== --- lnt/trunk/docs/tests.rst +++ lnt/trunk/docs/tests.rst @@ -305,3 +305,49 @@ This will run the test-suite many times over, collecting useful information in a report directory. The report collects many things like execution profiles, compiler time reports, intermediate files, binary files, and build information. + + +Cross-compiling ++++++++++++++++ + +The best way to run the test-suite in a cross-compiling setup with the +cmake driver is to use cmake's built-in support for cross-compiling as much as +possible. In practice, the recommended way to cross-compile is to use a cmake +toolchain file (see +https://cmake.org/cmake/help/v3.0/manual/cmake-toolchains.7.html#cross-compiling) + +An example command line for cross-compiling on an X86 machine, targeting +AArch64 linux, is:: + + $ lnt runtest test-suite \ + --sandbox SANDBOX \ + --test-suite /work/llvm-test-suite \ + --use-lit lit \ + --cppflags="-O3" \ + --run-under=$HOME/dev/aarch64-emu/aarch64-qemu.sh \ + --cmake-define=CMAKE_TOOLCHAIN_FILE:FILEPATH=$HOME/clang_aarch64_linux.cmake + +The key part here is the CMAKE_TOOLCHAIN_FILE define. As you're +cross-compiling, you may need a --run-under command as the produced binaries +probably won't run natively on your development machine, but something extra +needs to be done (e.g. running under a qemu simulator, or transferring the +binaries to a development board). This isn't explained further here. + +In your toolchain file, it's important to specify that the cmake variables +defining the toolchain must be cached in CMakeCache.txt, as that's where lnt +reads them from to figure out which compiler was used when needing to construct +metadata for the json report. An example is below. The important keywords to +make the variables appear in the CMakeCache.txt are "CACHE STRING "" FORCE":: + + $ cat clang_aarch64_linux.cmake + set(CMAKE_SYSTEM_NAME Linux ) + set(triple aarch64-linux-gnu ) + set(CMAKE_C_COMPILER /home/user/build/bin/clang CACHE STRING "" FORCE) + set(CMAKE_C_COMPILER_TARGET ${triple} CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER /home/user/build/bin/clang++ CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER_TARGET ${triple} CACHE STRING "" FORCE) + set(CMAKE_SYSROOT /home/user/aarch64-emu/sysroot-glibc-linaro-2.23-2016.11-aarch64-linux-gnu ) + set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN /home/user/aarch64-emu/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gnu ) + set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN /home/user/aarch64-emu/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gnu ) + + Index: lnt/trunk/lnt/tests/test_suite.py =================================================================== --- lnt/trunk/lnt/tests/test_suite.py +++ lnt/trunk/lnt/tests/test_suite.py @@ -220,19 +220,6 @@ type=str, default=None, help="Path to the C++ compiler to test (inferred from" " --cc where possible") - group.add_option("", "--llvm-arch", dest="llvm_arch", - type='choice', default=None, - help="Override the CMake-inferred architecture", - choices=TEST_SUITE_KNOWN_ARCHITECTURES) - group.add_option("", "--cross-compiling", dest="cross_compiling", - action="store_true", default=False, - help="Inform CMake that it should be cross-compiling") - group.add_option("", "--cross-compiling-system-name", type=str, - default=None, dest="cross_compiling_system_name", - help="The parameter to pass to CMAKE_SYSTEM_NAME when" - " cross-compiling. By default this is 'Linux' " - "unless -arch is in the cflags, in which case " - "it is 'Darwin'") group.add_option("", "--cppflags", type=str, action="append", dest="cppflags", default=[], help="Extra flags to pass the compiler in C or C++ mode. " @@ -359,34 +346,30 @@ else: parser.error("Expected no positional arguments (got: %r)" % (args,)) - for a in ['cross_compiling', 'cross_compiling_system_name', 'llvm_arch']: - if getattr(opts, a): - parser.error('option "%s" is not yet implemented!' % a) - if self.opts.sandbox_path is None: parser.error('--sandbox is required') - if self.opts.cc is None: - parser.error('--cc is required') - - # Option validation. - opts.cc = resolve_command_path(opts.cc) + if self.opts.cc is not None: + self.opts.cc = resolve_command_path(self.opts.cc) - if not lnt.testing.util.compilers.is_valid(opts.cc): - parser.error('--cc does not point to a valid executable.') + if not lnt.testing.util.compilers.is_valid(self.opts.cc): + parser.error('--cc does not point to a valid executable.') - # If there was no --cxx given, attempt to infer it from the --cc. - if opts.cxx is None: - opts.cxx = lnt.testing.util.compilers.infer_cxx_compiler(opts.cc) - if opts.cxx is not None: - note("Inferred C++ compiler under test as: %r" % (opts.cxx,)) + # If there was no --cxx given, attempt to infer it from the --cc. + if self.opts.cxx is None: + self.opts.cxx = \ + lnt.testing.util.compilers.infer_cxx_compiler(self.opts.cc) + if self.opts.cxx is not None: + note("Inferred C++ compiler under test as: %r" + % (self.opts.cxx,)) + else: + parser.error("unable to infer --cxx - set it manually.") else: - parser.error("unable to infer --cxx - set it manually.") - else: - opts.cxx = resolve_command_path(opts.cxx) + self.opts.cxx = resolve_command_path(self.opts.cxx) - if not os.path.exists(opts.cxx): - parser.error("invalid --cxx argument %r, does not exist" % (opts.cxx)) + if not os.path.exists(self.opts.cxx): + parser.error("invalid --cxx argument %r, does not exist" + % (self.opts.cxx)) if opts.test_suite_root is None: parser.error('--test-suite is required') @@ -471,6 +454,14 @@ # output. self._configure_if_needed() + # Verify that we can actually find a compiler before continuing + cmake_vars = self._extract_cmake_vars_from_cache() + if "CMAKE_C_COMPILER" not in cmake_vars or \ + not os.path.exists(cmake_vars["CMAKE_C_COMPILER"]): + parser.error( + "Couldn't find C compiler (%s). Maybe you should specify --cc?" + % cmake_vars.get("CMAKE_C_COMPILER")) + # We don't support compiling without testing as we can't get compile- # time numbers from LIT without running the tests. if opts.compile_multisample > opts.exec_multisample: @@ -480,7 +471,7 @@ if opts.auto_name: # Construct the nickname from a few key parameters. - cc_info = self._get_cc_info() + cc_info = self._get_cc_info(cmake_vars) cc_nick = '%s_%s' % (cc_info['cc_name'], cc_info['cc_build']) self.nick += "__%s__%s" % (cc_nick, cc_info['cc_target'].split('-')[0]) @@ -490,7 +481,7 @@ # is a horrible failure mode because all of our data ends up going # to order 0. The user needs to give an order if we can't detect! if opts.run_order is None: - cc_info = self._get_cc_info() + cc_info = self._get_cc_info(cmake_vars) if cc_info['inferred_run_order'] == 0: fatal("Cannot detect compiler version. Specify --run-order" " to manually define it.") @@ -501,7 +492,7 @@ for i in range(max(opts.exec_multisample, opts.compile_multisample)): c = i < opts.compile_multisample e = i < opts.exec_multisample - run_report, json_data = self.run(compile=c, test=e) + run_report, json_data = self.run(cmake_vars, compile=c, test=e) reports.append(run_report) json_reports.append(json_data) @@ -534,7 +525,7 @@ self._clean(self._base_path) self.configured = True - def run(self, compile=True, test=True): + def run(self, cmake_vars, compile=True, test=True): mkdir_p(self._base_path) if self.opts.pgo: @@ -551,7 +542,7 @@ self.compiled = True data = self._lit(self._base_path, test) - return self._parse_lit_output(self._base_path, data), data + return self._parse_lit_output(self._base_path, data, cmake_vars), data def _create_merged_report(self, reports): if len(reports) == 1: @@ -600,11 +591,12 @@ def _configure(self, path, extra_cmake_defs=[], execute=True): cmake_cmd = self.opts.cmake - defs = { - # FIXME: Support ARCH, SMALL/LARGE etc - 'CMAKE_C_COMPILER': self.opts.cc, - 'CMAKE_CXX_COMPILER': self.opts.cxx, - } + defs = {} + if self.opts.cc: + defs['CMAKE_C_COMPILER'] = self.opts.cc + if self.opts.cxx: + defs['CMAKE_CXX_COMPILER'] = self.opts.cxx, + if self.opts.cppflags or self.opts.cflags: all_cflags = ' '.join([self.opts.cppflags, self.opts.cflags]) defs['CMAKE_C_FLAGS'] = self._unix_quote_args(all_cflags) @@ -759,44 +751,55 @@ name = raw_name.rsplit('.test', 1)[0] return not os.path.exists(os.path.join(path, name)) - def _get_target_flags(self, cmake_vars): - build_type = cmake_vars["build_type"] - cflags = cmake_vars["c_flags"] - if build_type != "": - cflags = \ - " ".join(cflags.split(" ") + - cmake_vars["c_flags_"+build_type.lower()].split(" ")) - return shlex.split(cflags) - - def _get_cc_info(self): + def _extract_cmake_vars_from_cache(self): assert self.configured is True cmake_lah_output = self._check_output( [self.opts.cmake] + ['-LAH', '-N'] + [self._base_path]) pattern2var = [ - (re.compile("^%s:[^=]*=(.*)$" % cmakevar), var) - for cmakevar, var in ( - ("CMAKE_C_COMPILER", "cc"), - ("CMAKE_BUILD_TYPE", "build_type"), - ("CMAKE_CXX_FLAGS", "cxx_flags"), - ("CMAKE_CXX_FLAGS_DEBUG", "cxx_flags_debug"), - ("CMAKE_CXX_FLAGS_MINSIZEREL", "cxx_flags_minsizerel"), - ("CMAKE_CXX_FLAGS_RELEASE", "cxx_flags_release"), - ("CMAKE_CXX_FLAGS_RELWITHDEBINFO", "cxx_flags_relwithdebinfo"), - ("CMAKE_C_FLAGS", "c_flags"), - ("CMAKE_C_FLAGS_DEBUG", "c_flags_debug"), - ("CMAKE_C_FLAGS_MINSIZEREL", "c_flags_minsizerel"), - ("CMAKE_C_FLAGS_RELEASE", "c_flags_release"), - ("CMAKE_C_FLAGS_RELWITHDEBINFO", "c_flags_relwithdebinfo"),)] + (re.compile("^%s:[^=]*=(.*)$" % cmakevar), cmakevar) + for cmakevar in ( + "CMAKE_C_COMPILER", + "CMAKE_BUILD_TYPE", + "CMAKE_CXX_FLAGS", + "CMAKE_CXX_FLAGS_DEBUG", + "CMAKE_CXX_FLAGS_MINSIZEREL", + "CMAKE_CXX_FLAGS_RELEASE", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO", + "CMAKE_C_FLAGS", + "CMAKE_C_FLAGS_DEBUG", + "CMAKE_C_FLAGS_MINSIZEREL", + "CMAKE_C_FLAGS_RELEASE", + "CMAKE_C_FLAGS_RELWITHDEBINFO", + "CMAKE_C_COMPILER_TARGET", + "CMAKE_CXX_COMPILER_TARGET", + )] cmake_vars = {} for line in cmake_lah_output.split("\n"): for pattern, varname in pattern2var: m = re.match(pattern, line) if m: cmake_vars[varname] = m.group(1) + return cmake_vars + + def _get_cc_info(self, cmake_vars): + build_type = cmake_vars["CMAKE_BUILD_TYPE"] + cflags = cmake_vars["CMAKE_C_FLAGS"] + if build_type != "": + cflags = \ + " ".join(cflags.split(" ") + + cmake_vars["CMAKE_C_FLAGS_" + build_type.upper()] + .split(" ")) + # FIXME: this probably needs to be conditionalized on the compiler + # being clang. Or maybe we need an + # lnt.testing.util.compilers.get_cc_info uses cmake somehow? + if "CMAKE_C_COMPILER_TARGET" in cmake_vars: + cflags += " --target=" + cmake_vars["CMAKE_C_COMPILER_TARGET"] + target_flags = shlex.split(cflags) + return lnt.testing.util.compilers.get_cc_info( - cmake_vars["cc"], self._get_target_flags(cmake_vars)) + cmake_vars["CMAKE_C_COMPILER"], target_flags) - def _parse_lit_output(self, path, data, only_test=False): + def _parse_lit_output(self, path, data, cmake_vars, only_test=False): LIT_METRIC_TO_LNT = { 'compile_time': 'compile', 'exec_time': 'exec', @@ -901,7 +904,7 @@ run_info = { 'tag': 'nts' } - run_info.update(self._get_cc_info()) + run_info.update(self._get_cc_info(cmake_vars)) run_info['run_order'] = run_info['inferred_run_order'] if self.opts.run_order: run_info['run_order'] = self.opts.run_order Index: lnt/trunk/tests/SharedInputs/FakeCompilers/fakecompiler.py =================================================================== --- lnt/trunk/tests/SharedInputs/FakeCompilers/fakecompiler.py +++ lnt/trunk/tests/SharedInputs/FakeCompilers/fakecompiler.py @@ -21,7 +21,7 @@ def print_dumpmachine(self): raise NotImplementedError - def print_llvm_target(self): + def print_llvm_target(self, args): raise NotImplementedError def print_as_version(self): @@ -40,7 +40,7 @@ ... more boring stuff here ... """ - def print_llvm_target(self): + def print_llvm_target(self, args): print """\ icc: command line warning #10006: ignoring unknown option '-flto' .file "null" @@ -53,14 +53,18 @@ print """i686-apple-darwin11""" class LLVMCompiler(FakeCompiler): - def print_llvm_target(self): + def print_llvm_target(self, args): + target = "x86_64-apple-darwin11.0.0" + for arg in args: + if arg.startswith("--target="): + target = arg[len("--target="):] print """\ ; ModuleID = '/dev/null' target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-\ f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-\ n8:16:32:64" -target triple = "x86_64-apple-darwin11.0.0" -""" +target triple = "%s" +""" % target def print_dumpmachine(self): print """x86_64-apple-darwin11.0.0""" @@ -201,7 +205,7 @@ elif 'Wl,-v' in args: compiler_instance.print_ld_version() elif args_contained_in(args, ('-S', '-flto', '-o', '-', '/dev/null')): - compiler_instance.print_llvm_target() + compiler_instance.print_llvm_target(args) else: raise SystemExit("unrecognized argument vector: %r" % ( args,)) Index: lnt/trunk/tests/runtest/Inputs/test-suite-cmake/fake-cmake =================================================================== --- lnt/trunk/tests/runtest/Inputs/test-suite-cmake/fake-cmake +++ lnt/trunk/tests/runtest/Inputs/test-suite-cmake/fake-cmake @@ -4,6 +4,7 @@ DUMP_VARS=false CMAKE_C_COMPILER="" CMAKE_CXX_COMPILER="" +CMAKE_C_COMPILER_TARGET="notfound" while test $# -gt 0 do @@ -16,6 +17,7 @@ # handle -D arguments to cmake. if [[ $1 == -DCMAKE_C_COMPILER:* ]]; then CMAKE_C_COMPILER=${1#*=}; fi if [[ $1 == -DCMAKE_CXX_COMPILER:* ]]; then CMAKE_CXX_COMPILER=${1#*=}; fi + if [[ $1 == -DCMAKE_C_COMPILER_TARGET:* ]]; then CMAKE_C_COMPILER_TARGET=${1#*=}; fi shift elif [[ $1 == -LAH && $2 == -N ]]; then DUMP_VARS=true @@ -31,6 +33,7 @@ if [[ -f $1/CMakeCache.txt ]]; then CMAKE_C_COMPILER=`grep CMAKE_C_COMPILER:FILEPATH= $1/CMakeCache.txt | cut -f2 -d'='` CMAKE_CXX_COMPILER=`grep CMAKE_CXX_COMPILER:FILEPATH= $1/CMakeCache.txt | cut -f2 -d'='` + CMAKE_C_COMPILER_TARGET=`grep CMAKE_C_COMPILER_TARGET:STRING= $1/CMakeCache.txt | cut -f2 -d'='` fi shift fi @@ -40,6 +43,9 @@ then echo CMAKE_C_COMPILER:FILEPATH=$CMAKE_C_COMPILER echo CMAKE_CXX_COMPILER:FILEPATH=$CMAKE_CXX_COMPILER + if [[ $CMAKE_C_COMPILER_TARGET != "notfound" ]]; then + echo CMAKE_C_COMPILER_TARGET:STRING=$CMAKE_C_COMPILER_TARGET + fi echo CMAKE_BUILD_TYPE:STRING=RelWithDebInfo echo CMAKE_C_FLAGS:STRING=-O0 echo CMAKE_CXX_FLAGS:STRING= @@ -50,6 +56,9 @@ echo "Dummy" > CMakeCache.txt echo CMAKE_C_COMPILER:FILEPATH=$CMAKE_C_COMPILER >> CMakeCache.txt echo CMAKE_CXX_COMPILER:FILEPATH=$CMAKE_CXX_COMPILER >> CMakeCache.txt + if [[ $CMAKE_C_COMPILER_TARGET != "notfound" ]]; then + echo CMAKE_C_COMPILER_TARGET:STRING=$CMAKE_C_COMPILER_TARGET >> CMakeCache.txt + fi mkdir subtest cp $CMAKE_SRC_DIR/fake-test $CMAKE_SRC_DIR/fake-results.json subtest fi Index: lnt/trunk/tests/runtest/test_suite.py =================================================================== --- lnt/trunk/tests/runtest/test_suite.py +++ lnt/trunk/tests/runtest/test_suite.py @@ -378,7 +378,7 @@ # RUN: --use-lit %S/Inputs/test-suite-cmake/fake-lit \ # RUN: > %t.log 2> %t.err || true # RUN: FileCheck --check-prefix CHECK-MISSING-CC < %t.err %s -# CHECK-MISSING-CC: error: --cc is required +# CHECK-MISSING-CC: error: Couldn't find C compiler (). Maybe you should specify --cc? # Check on conflicting -cc and -cmake-define=CMAKE_C_COMPILER # options, the right compiler gets stored in the json report @@ -395,6 +395,20 @@ # RUN: FileCheck --check-prefix CHECK-CC-CONFL-CMAKEDEFINE < %t.SANDBOX/build/report.json %s # CHECK-CC-CONFL-CMAKEDEFINE: "run_order": "154332" +# Check that while cross-compiling, the target architecture is recognized +# correctly. +# RUN: lnt runtest test-suite \ +# RUN: --sandbox %t.SANDBOX \ +# RUN: --no-timestamp \ +# RUN: --test-suite %S/Inputs/test-suite-cmake \ +# RUN: --cmake-define=CMAKE_C_COMPILER_TARGET:STRING=targetarch-linux-gnu \ +# RUN: --cc %{shared_inputs}/FakeCompilers/clang-r154331 \ +# RUN: --use-cmake %S/Inputs/test-suite-cmake/fake-cmake \ +# RUN: --use-make %S/Inputs/test-suite-cmake/fake-make \ +# RUN: --use-lit %S/Inputs/test-suite-cmake/fake-lit \ +# RUN: > %t.log 2> %t.err || true +# RUN: FileCheck --check-prefix CHECK-CROSS-TARGET < %t.SANDBOX/build/report.json %s +# CHECK-CROSS-TARGET: "cc_target": "targetarch-linux-gnu" # Check running with PGO # RUN: lnt runtest test-suite \