From 9fa5f73d00905568e8979d0c93ec4a8f645c81d5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 9 Aug 2016 11:31:53 +0200 Subject: Implement support for C compilation We now have two new modules: cc (c-common) and c. --- build2/cxx/guess.cxx | 948 --------------------------------------------------- 1 file changed, 948 deletions(-) delete mode 100644 build2/cxx/guess.cxx (limited to 'build2/cxx/guess.cxx') diff --git a/build2/cxx/guess.cxx b/build2/cxx/guess.cxx deleted file mode 100644 index 11a832c..0000000 --- a/build2/cxx/guess.cxx +++ /dev/null @@ -1,948 +0,0 @@ -// file : build2/cxx/guess.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include // strlen() - -#include - -using namespace std; - -namespace build2 -{ - namespace cxx - { - // Pre-guess the compiler type based on the compiler executable name. - // Return empty string if can't make a guess (for example, because the - // compiler name is a generic 'c++'). Note that it only guesses the type, - // not the variant. - // - static string - pre_guess (const path& cxx) - { - tracer trace ("cxx::pre_guess"); - - const string s (cxx.leaf ().base ().string ()); - size_t n (s.size ()); - - // Name separator characters (e.g., '-' in 'g++-4.8'). - // - auto sep = [] (char c) -> bool - { - return c == '-' || c == '_' || c == '.'; - }; - - auto stem = [&sep, &s, n] (const char* x) -> bool - { - size_t m (strlen (x)); - size_t p (s.find (x, 0, m)); - - return p != string::npos && - (p == 0 || sep (s[p - 1])) && // Separated at the beginning. - ((p += m) == n || sep (s[p])); // Separated at the end. - }; - - if (stem ("g++")) - return "gcc"; - - if (stem ("clang++")) - return "clang"; - - if (stem ("icpc")) - return "icc"; - - // Keep this one last since 'cl' is very generic. - // - if (stem ("cl")) - return "msvc"; - - // Warn if the user specified a C compiler instead of C++. - // - if (stem ("gcc")) - { - warn << cxx << " looks like a C compiler" << - info << "should it be 'g++' instead of 'gcc'?"; - } - else if (stem ("clang")) - { - warn << cxx << " looks like a C compiler" << - info << "should it be 'clang++' instead of 'clang'?"; - } - else if (stem ("icc")) - { - warn << cxx << " looks like a C compiler" << - info << "should it be 'icpc' instead of 'icc'?"; - } - - l4 ([&]{trace << "unable to guess compiler type of " << cxx;}); - return ""; - } - - // Guess the compiler type and variant by running it. If the pre argument - // is not empty, then only "confirm" the pre-guess. Return empty result if - // unable to guess. - // - struct guess_result - { - compiler_id id; - string signature; - string checksum; - - bool - empty () const {return id.empty ();} - }; - - static guess_result - guess (const path& cxx, const string& pre) - { - tracer trace ("cxx::guess"); - - guess_result r; - - // Start with -v. This will cover gcc and clang. - // - // While icc also writes what may seem like something we can use to - // detect it: - // - // icpc version 16.0.2 (gcc version 4.9.0 compatibility) - // - // That first word is actually the executable name. So if we rename - // icpc to foocpc, we will get: - // - // foocpc version 16.0.2 (gcc version 4.9.0 compatibility) - // - // In fact, if someone renames icpc to g++, there will be no way for - // us to detect this. Oh, well, their problem. - // - if (r.id.empty () && (pre.empty () || pre == "gcc" || pre == "clang")) - { - auto f = [] (string& l) -> guess_result - { - // The g++ -v output will have a line (currently last) in the form: - // - // "gcc version X.Y.Z ..." - // - // The "version" word can probably be translated. For example: - // - // gcc version 3.4.4 - // gcc version 4.2.1 - // gcc version 4.8.2 (GCC) - // gcc version 4.8.5 (Ubuntu 4.8.5-2ubuntu1~14.04.1) - // gcc version 4.9.2 (Ubuntu 4.9.2-0ubuntu1~14.04) - // gcc version 5.1.0 (Ubuntu 5.1.0-0ubuntu11~14.04.1) - // gcc version 6.0.0 20160131 (experimental) (GCC) - // - if (l.compare (0, 4, "gcc ") == 0) - return guess_result {{"gcc", ""}, move (l), ""}; - - // The Apple clang++ -v output will have a line (currently first) - // in the form: - // - // "Apple (LLVM|clang) version X.Y.Z ..." - // - // Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn) - // Apple clang version 4.0 (tags/Apple/clang-421.0.60) (based on LLVM 3.1svn) - // Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn) - // Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn) - // Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn) - // Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn) - // Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) - // Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn) - // Apple LLVM version 7.0.0 (clang-700.0.53) - // Apple LLVM version 7.0.0 (clang-700.1.76) - // Apple LLVM version 7.0.2 (clang-700.1.81) - // Apple LLVM version 7.3.0 (clang-703.0.16.1) - // - // Note that the g++ "alias" for clang++ also includes this line - // but it is (currently) preceded by "Configured with: ...". - // - // Check for Apple clang before the vanilla one since the above - // line also includes "clang". - // - if (l.compare (0, 6, "Apple ") == 0 && - (l.compare (6, 5, "LLVM ") == 0 || - l.compare (6, 6, "clang ") == 0)) - return guess_result {{"clang", "apple"}, move (l), ""}; - - // The vanilla clang++ -v output will have a line (currently first) - // in the form: - // - // "[... ]clang version X.Y.Z[-...] ..." - // - // The "version" word can probably be translated. For example: - // - // FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512 - // Ubuntu clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) (based on LLVM 3.5.0) - // Ubuntu clang version 3.6.0-2ubuntu1~trusty1 (tags/RELEASE_360/final) (based on LLVM 3.6.0) - // clang version 3.7.0 (tags/RELEASE_370/final) - // - if (l.find ("clang ") != string::npos) - return guess_result {{"clang", ""}, move (l), ""}; - - return guess_result (); - }; - - // The -v output contains other information (such as the compiler - // build configuration for gcc or the selected gcc installation for - // clang) which makes sense to include into the compiler checksum. So - // ask run() to calculate it for every line of the -v ouput. - // - sha256 cs; - - // Suppress all the compiler errors because we may be trying an - // unsupported option. - // - r = run (cxx, "-v", f, false, false, &cs); - - if (!r.empty ()) - r.checksum = cs.string (); - } - - // Next try --version to detect icc. - // - if (r.empty () && (pre.empty () || pre == "icc")) - { - auto f = [] (string& l) -> guess_result - { - // The first line has the " (ICC) " in it, for example: - // - // icpc (ICC) 9.0 20060120 - // icpc (ICC) 11.1 20100414 - // icpc (ICC) 12.1.0 20110811 - // icpc (ICC) 14.0.0 20130728 - // icpc (ICC) 15.0.2 20150121 - // icpc (ICC) 16.0.2 20160204 - // - if (l.find (" (ICC) ") != string::npos) - return guess_result {{"icc", ""}, move (l), ""}; - - return guess_result (); - }; - - r = run (cxx, "--version", f, false); - } - - // Finally try to run it without any options to detect msvc. - // - // - if (r.empty () && (pre.empty () || pre == "msvc")) - { - auto f = [] (string& l) -> guess_result - { - // Check for "Microsoft (R)" and "C/C++" in the first line as a - // signature since all other words/positions can be translated. For - // example: - // - // Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.6030 for 80x86 - // Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86 - // Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 - // Compilador de optimizacion de C/C++ de Microsoft (R) version 16.00.30319.01 para x64 - // Microsoft (R) C/C++ Optimizing Compiler Version 17.00.50727.1 for x86 - // Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86 - // Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23026 for x86 - // - // In the recent versions the architecture is either "x86", "x64", - // or "ARM". - // - if (l.find ("Microsoft (R)") != string::npos && - l.find ("C/C++") != string::npos) - return guess_result {{"msvc", ""}, move (l), ""}; - - return guess_result (); - }; - - r = run (cxx, f, false); - } - - if (!r.empty ()) - { - if (!pre.empty () && r.id.type != pre) - { - l4 ([&]{trace << "compiler type guess mismatch" - << ", pre-guessed " << pre - << ", determined " << r.id.type;}); - - r = guess_result (); - } - else - l5 ([&]{trace << cxx << " is " << r.id << ": '" - << r.signature << "'";}); - } - else - l4 ([&]{trace << "unable to determine compiler type of " << cxx;}); - - return r; - } - - static compiler_info - guess_gcc (const path& cxx, const strings* coptions, guess_result&& gr) - { - tracer trace ("cxx::guess_gcc"); - - // Extract the version. The signature line has the following format - // though language words can be translated and even rearranged (see - // examples above). - // - // "gcc version A.B.C[ ...]" - // - string& s (gr.signature); - - // Scan the string as words and look for one that looks like a version. - // - size_t b (0), e (0); - while (next_word (s, b, e)) - { - // The third argument to find_first_not_of() is the length of the - // first argument, not the length of the interval to check. So to - // limit it to [b, e) we are also going to compare the result to the - // end of the word position (first space). In fact, we can just check - // if it is >= e. - // - if (s.find_first_not_of ("1234567890.", b, 11) >= e) - break; - } - - if (b == e) - fail << "unable to extract gcc version from '" << s << "'"; - - compiler_version v; - v.string.assign (s, b, string::npos); - - // Split the version into components. - // - size_t vb (b), ve (b); - auto next = [&s, b, e, &vb, &ve] (const char* m) -> uint64_t - { - try - { - if (next_word (s, e, vb, ve, '.')) - return stoull (string (s, vb, ve - vb)); - } - catch (const invalid_argument&) {} - catch (const out_of_range&) {} - - error << "unable to extract gcc " << m << " version from '" - << string (s, b, e - b) << "'"; - throw failed (); - }; - - v.major = next ("major"); - v.minor = next ("minor"); - v.patch = next ("patch"); - - if (e != s.size ()) - v.build.assign (s, e + 1, string::npos); - - // Figure out the target architecture. This is actually a lot trickier - // than one would have hoped. - // - // There is the -dumpmachine option but gcc doesn't adjust it per the - // compile options (e.g., -m32). However, starting with 4.6 it has the - // -print-multiarch option which gives (almost) the right answer. The - // "almost" part has to do with it not honoring the -arch option (which - // is really what this compiler is building for). To get to that, we - // would have to resort to a hack like this: - // - // gcc -v -E - 2>&1 | grep cc1 - // .../cc1 ... -mtune=generic -march=x86-64 - // - // Also, -print-multiarch will print am empty line if the compiler - // actually wasn't built with multi-arch support. - // - // So for now this is what we are going to do for the time being: First - // try -print-multiarch. If that works out (recent gcc configure with - // multi-arch support), then use the result. Otherwise, fallback to - // -dumpmachine (older gcc or not multi-arch). - // - cstrings args {cxx.string ().c_str (), "-print-multiarch"}; - if (coptions != nullptr) - append_options (args, *coptions); - args.push_back (nullptr); - - // The output of both -print-multiarch and -dumpmachine is a single line - // containing just the target triplet. - // - auto f = [] (string& l) {return move (l);}; - - string t (run (args.data (), f, false)); - - if (t.empty ()) - { - l5 ([&]{trace << cxx << " doesn's support -print-multiarch, " - << "falling back to -dumpmachine";}); - - args[1] = "-dumpmachine"; - t = run (args.data (), f); - } - - if (t.empty ()) - fail << "unable to extract target architecture from " << cxx - << " -print-multiarch or -dumpmachine output"; - - return compiler_info { - move (gr.id), - move (v), - move (gr.signature), - move (gr.checksum), // Calculated on whole -v output. - move (t)}; - } - - static compiler_info - guess_clang (const path& cxx, const strings* coptions, guess_result&& gr) - { - // Extract the version. Here we will try to handle both vanilla and - // Apple clang since the signature lines are fairly similar. They have - // the following format though language words can probably be translated - // and even rearranged (see examples above). - // - // "[... ]clang version A.B.C[( |-)...]" - // "Apple (clang|LLVM) version A.B[.C] ..." - // - string& s (gr.signature); - - // Some overrides for testing. - // - //s = "clang version 3.7.0 (tags/RELEASE_370/final)"; - // - //gr.id.variant = "apple"; - //s = "Apple LLVM version 7.3.0 (clang-703.0.16.1)"; - //s = "Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn)"; - - // Scan the string as words and look for one that looks like a version. - // Use '-' as a second delimiter to handle versions like - // "3.6.0-2ubuntu1~trusty1". - // - size_t b (0), e (0); - while (next_word (s, b, e, ' ', '-')) - { - // The third argument to find_first_not_of() is the length of the - // first argument, not the length of the interval to check. So to - // limit it to [b, e) we are also going to compare the result to the - // end of the word position (first space). In fact, we can just check - // if it is >= e. - // - if (s.find_first_not_of ("1234567890.", b, 11) >= e) - break; - } - - if (b == e) - fail << "unable to extract clang version from '" << s << "'"; - - compiler_version v; - v.string.assign (s, b, string::npos); - - // Split the version into components. - // - size_t vb (b), ve (b); - auto next = [&s, b, e, &vb, &ve] (const char* m, bool opt) -> uint64_t - { - try - { - if (next_word (s, e, vb, ve, '.')) - return stoull (string (s, vb, ve - vb)); - - if (opt) - return 0; - } - catch (const invalid_argument&) {} - catch (const out_of_range&) {} - - error << "unable to extract clang " << m << " version from '" - << string (s, b, e - b) << "'"; - throw failed (); - }; - - v.major = next ("major", false); - v.minor = next ("minor", false); - v.patch = next ("patch", gr.id.variant == "apple"); - - if (e != s.size ()) - v.build.assign (s, e + 1, string::npos); - - // Figure out the target architecture. - // - // Unlike gcc, clang doesn't have -print-multiarch. Its -dumpmachine, - // however, respects the compile options (e.g., -m32). - // - cstrings args {cxx.string ().c_str (), "-dumpmachine"}; - if (coptions != nullptr) - append_options (args, *coptions); - args.push_back (nullptr); - - // The output of -dumpmachine is a single line containing just the - // target triplet. - // - string t (run (args.data (), [] (string& l) {return move (l);})); - - if (t.empty ()) - fail << "unable to extract target architecture from " << cxx - << " -dumpmachine output"; - - return compiler_info { - move (gr.id), - move (v), - move (gr.signature), - move (gr.checksum), // Calculated on whole -v output. - move (t)}; - } - - static compiler_info - guess_icc (const path& cxx, const strings* coptions, guess_result&& gr) - { - // Extract the version. If the version has the fourth component, then - // the signature line (extracted with --version) won't include it. So we - // will have to get a more elaborate line with -V. We will also have to - // do it to get the compiler target that respects the -m option: icc - // doesn't support -print-multiarch like gcc and its -dumpmachine - // doesn't respect -m like clang. In fact, its -dumpmachine is - // completely broken as it appears to print the compiler's host and not - // the target (e.g., .../bin/ia32/icpc prints x86_64-linux-gnu). - // - // Some examples of the signature lines from -V output: - // - // Intel(R) C++ Compiler for 32-bit applications, Version 9.1 Build 20070215Z Package ID: l_cc_c_9.1.047 - // Intel(R) C++ Compiler for applications running on Intel(R) 64, Version 10.1 Build 20071116 - // Intel(R) C++ Compiler for applications running on IA-32, Version 10.1 Build 20071116 Package ID: l_cc_p_10.1.010 - // Intel C++ Intel 64 Compiler Professional for applications running on Intel 64, Version 11.0 Build 20081105 Package ID: l_cproc_p_11.0.074 - // Intel(R) C++ Intel(R) 64 Compiler Professional for applications running on Intel(R) 64, Version 11.1 Build 20091130 Package ID: l_cproc_p_11.1.064 - // Intel C++ Intel 64 Compiler XE for applications running on Intel 64, Version 12.0.4.191 Build 20110427 - // Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.2.181 Build 20160204 - // Intel(R) C++ Intel(R) 64 Compiler for applications running on IA-32, Version 16.0.2.181 Build 20160204 - // Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) MIC Architecture, Version 16.0.2.181 Build 20160204 - // - // We should probably also assume the language words can be translated - // and even rearranged. - // - string& s (gr.signature); - s.clear (); - - auto f = [] (string& l) - { - return l.compare (0, 5, "Intel") == 0 && (l[5] == '(' || l[5] == ' ') - ? move (l) - : string (); - }; - - // The -V output is sent to STDERR. - // - s = run (cxx, "-V", f, false); - - if (s.empty ()) - fail << "unable to extract signature from " << cxx << " -V output"; - - if (s.find ("C++") == string::npos) - fail << cxx << " does not appear to be the Intel C++ compiler" << - info << "extracted signature: '" << s << "'"; - - // Scan the string as words and look for the version. It consist of only - // digits and periods and contains at least one period. - // - - // Some overrides for testing. - // - //s = "Intel(R) C++ Compiler for 32-bit applications, Version 9.1 Build 20070215Z Package ID: l_cc_c_9.1.047"; - //s = "Intel(R) C++ Compiler for applications running on Intel(R) 64, Version 10.1 Build 20071116"; - //s = "Intel(R) C++ Compiler for applications running on IA-32, Version 10.1 Build 20071116 Package ID: l_cc_p_10.1.010"; - //s = "Intel C++ Intel 64 Compiler Professional for applications running on Intel 64, Version 11.0 Build 20081105 Package ID: l_cproc_p_11.0.074"; - //s = "Intel(R) C++ Intel(R) 64 Compiler Professional for applications running on Intel(R) 64, Version 11.1 Build 20091130 Package ID: l_cproc_p_11.1.064"; - //s = "Intel C++ Intel 64 Compiler XE for applications running on Intel 64, Version 12.0.4.191 Build 20110427"; - - size_t b (0), e (0), n; - while (next_word (s, b, e, ' ', ',') != 0) - { - // The third argument to find_first_not_of() is the length of the - // first argument, not the length of the interval to check. So to - // limit it to [b, e) we are also going to compare the result to the - // end of the word position (first space). In fact, we can just check - // if it is >= e. Similar logic for find_first_of() except that we add - // space to the list of character to make sure we don't go too far. - // - if (s.find_first_not_of ("1234567890.", b, 11) >= e && - s.find_first_of (". ", b, 2) < e) - break; - } - - if (b == e) - fail << "unable to extract icc version from '" << s << "'"; - - compiler_version v; - v.string.assign (s, b, string::npos); - - // Split the version into components. - // - size_t vb (b), ve (b); - auto next = [&s, b, e, &vb, &ve] (const char* m, bool opt) -> uint64_t - { - try - { - if (next_word (s, e, vb, ve, '.')) - return stoull (string (s, vb, ve - vb)); - - if (opt) - return 0; - } - catch (const invalid_argument&) {} - catch (const out_of_range&) {} - - error << "unable to extract icc " << m << " version from '" - << string (s, b, e - b) << "'"; - throw failed (); - }; - - v.major = next ("major", false); - v.minor = next ("minor", false); - v.patch = next ("patch", true); - - if (vb != ve && next_word (s, e, vb, ve, '.')) - v.build.assign (s, vb, ve - vb); - - if (e != s.size ()) - { - if (!v.build.empty ()) - v.build += ' '; - - v.build.append (s, e + 1, string::npos); - } - - // Figure out the target CPU by re-running the compiler with -V and - // compile options (which may include, e.g., -m32). The output will - // contain two CPU keywords: the first is the host and the second is the - // target (hopefully this won't get rearranged by the translation). - // - // The CPU keywords (based on the above samples) appear to be: - // - // "32-bit" - // "IA-32" - // "Intel" "64" - // "Intel(R)" "64" - // "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux) - // - cstrings args {cxx.string ().c_str (), "-V"}; - if (coptions != nullptr) - append_options (args, *coptions); - args.push_back (nullptr); - - // The -V output is sent to STDERR. - // - string t (run (args.data (), f, false)); - - if (t.empty ()) - fail << "unable to extract target architecture from " << cxx - << " -V output"; - - string arch; - for (b = e = 0; (n = next_word (t, b, e, ' ', ',')) != 0; ) - { - if (t.compare (b, n, "Intel(R)", 8) == 0 || - t.compare (b, n, "Intel", 5) == 0) - { - if ((n = next_word (t, b, e, ' ', ',')) != 0) - { - if (t.compare (b, n, "64", 2) == 0) - { - arch = "x86_64"; - } - else if (t.compare (b, n, "MIC", 3) == 0) - { - arch = "x86_64"; // Plus "-k1om-linux" from -dumpmachine below. - } - } - else - break; - } - else if (t.compare (b, n, "IA-32", 5) == 0 || - t.compare (b, n, "32-bit", 6) == 0) - { - arch = "i386"; - } - } - - if (arch.empty ()) - fail << "unable to extract icc target architecture from '" << t << "'"; - - // So we have the CPU but we still need the rest of the triplet. While - // icc currently doesn't support cross-compilation (at least on Linux) - // and we could have just used the build triplet (i.e., the architecture - // on which we are running), who knows what will happen in the future. - // So instead we are going to use -dumpmachine and substitute the CPU. - // - t = run (cxx, "-dumpmachine", [] (string& l) {return move (l);}); - - if (t.empty ()) - fail << "unable to extract target architecture from " << cxx - << " -dumpmachine output"; - - // The first component in the triplet is always CPU. - // - size_t p (t.find ('-')); - - if (p == string::npos) - fail << "unable to parse icc target architecture '" << t << "'"; - - arch.append (t, p, string::npos); - - // Use the signature line to generate the checksum. - // - sha256 cs (s); - - return compiler_info { - move (gr.id), - move (v), - move (gr.signature), - cs.string (), - move (arch)}; - } - - static compiler_info - guess_msvc (const path&, guess_result&& gr) - { - // Extract the version. The signature line has the following format - // though language words can be translated and even rearranged (see - // examples above). - // - // "Microsoft (R) C/C++ Optimizing Compiler Version A.B.C[.D] for CPU" - // - // The CPU keywords (based on the above samples) appear to be: - // - // "80x86" - // "x86" - // "x64" - // "ARM" - // - string& s (gr.signature); - - // Some overrides for testing. - // - //s = "Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86"; - //s = "Compilador de optimizacion de C/C++ de Microsoft (R) version 16.00.30319.01 para x64"; - - // Scan the string as words and look for the version. While doing this - // also keep an eye on the CPU keywords. - // - string arch; - size_t b (0), e (0); - - auto check_cpu = [&arch, &s, &b, &e] () -> bool - { - size_t n (e - b); - - if (s.compare (b, n, "x64", 3) == 0 || - s.compare (b, n, "x86", 3) == 0 || - s.compare (b, n, "ARM", 3) == 0 || - s.compare (b, n, "80x86", 5) == 0) - { - arch.assign (s, b, n); - return true; - } - - return false; - }; - - while (next_word (s, b, e, ' ', ',')) - { - // First check for the CPU keywords in case in some language they come - // before the version. - // - if (check_cpu ()) - continue; - - // The third argument to find_first_not_of() is the length of the - // first argument, not the length of the interval to check. So to - // limit it to [b, e) we are also going to compare the result to the - // end of the word position (first space). In fact, we can just check - // if it is >= e. - // - if (s.find_first_not_of ("1234567890.", b, 11) >= e) - break; - } - - if (b == e) - fail << "unable to extract msvc version from '" << s << "'"; - - compiler_version v; - v.string.assign (s, b, e - b); - - // Split the version into components. - // - size_t vb (b), ve (b); - auto next = [&s, b, e, &vb, &ve] (const char* m) -> uint64_t - { - try - { - if (next_word (s, e, vb, ve, '.')) - return stoull (string (s, vb, ve - vb)); - } - catch (const invalid_argument&) {} - catch (const out_of_range&) {} - - error << "unable to extract msvc " << m << " version from '" - << string (s, b, e - b) << "'"; - throw failed (); - }; - - v.major = next ("major"); - v.minor = next ("minor"); - v.patch = next ("patch"); - - if (next_word (s, e, vb, ve, '.')) - v.build.assign (s, vb, ve - vb); - - // Continue scanning for the CPU. - // - if (e != s.size ()) - { - while (next_word (s, b, e, ' ', ',')) - { - if (check_cpu ()) - break; - } - } - - if (arch.empty ()) - fail << "unable to extract msvc target architecture from " - << "'" << s << "'"; - - // Now we need to map x86, x64, and ARM to the target triplets. The - // problem is, there aren't any established ones so we got to invent - // them ourselves. Based on the discussion in , we need - // something in the CPU-VENDOR-OS-ABI form. - // - // The CPU part is fairly straightforward with x86 mapped to 'i386' (or - // maybe 'i686'), x64 to 'x86_64', and ARM to 'arm' (it could also - // include the version, e.g., 'amrv8'). - // - // The (toolchain) VENDOR is also straightforward: 'microsoft'. Why not - // omit it? Two reasons: firstly, there are other compilers with the - // otherwise same target, for example Intel C++, and it could be useful - // to distinguish between them. Secondly, by having all four components - // we remove any parsing ambiguity. - // - // OS-ABI is where things are not as clear cut. The OS part shouldn't - // probably be just 'windows' since we have Win32 and WinCE. And WinRT. - // And Universal Windows Platform (UWP). So perhaps the following values - // for OS: 'win32', 'wince', 'winrt', 'winup'. - // - // For 'win32' the ABI part could signal the Microsoft C/C++ runtime by - // calling it 'msvc'. And seeing that the runtimes are incompatible from - // version to version, we should probably add the 'X.Y' version at the - // end (so we essentially mimic the DLL name, e.g, msvcr120.dll). Some - // suggested we also encode the runtime type (those /M* options) though - // I am not sure: the only "redistributable" runtime is multi-threaded - // release DLL. - // - // The ABI part for the other OS values needs thinking. For 'winrt' and - // 'winup' it probably makes sense to encode the WINAPI_FAMILY macro - // value (perhaps also with the version). Some of its values: - // - // WINAPI_FAMILY_APP Windows 10 - // WINAPI_FAMILY_PC_APP Windows 8.1 - // WINAPI_FAMILY_PHONE_APP Windows Phone 8.1 - // - // For 'wince' we may also want to add the OS version, e.g., 'wince4.2'. - // - // Putting it all together, Visual Studio 2015 will then have the - // following target triplets: - // - // x86 i386-microsoft-win32-msvc14.0 - // x64 x86_64-microsoft-win32-msvc14.0 - // ARM arm-microsoft-winup-??? - // - if (arch == "ARM") - fail << "cl.exe ARM/WinRT/UWP target is not yet supported"; - else - { - if (arch == "x64") - arch = "x86_64-microsoft-win32-msvc"; - else if (arch == "x86" || arch == "80x86") - arch = "i386-microsoft-win32-msvc"; - else - assert (false); - - // Mapping of compiler versions to runtime versions: - // - // 19.00 140/14.0 VS2015 - // 18.00 120/12.0 VS2013 - // 17.00 110/11.0 VS2012 - // 16.00 100/10.0 VS2010 - // 15.00 90/9.0 VS2008 - // 14.00 80/8.0 VS2005 - // 13.10 71/7.1 VS2003 - // - /**/ if (v.major == 19 && v.minor == 0) arch += "14.0"; - else if (v.major == 18 && v.minor == 0) arch += "12.0"; - else if (v.major == 17 && v.minor == 0) arch += "11.0"; - else if (v.major == 16 && v.minor == 0) arch += "10.0"; - else if (v.major == 15 && v.minor == 0) arch += "9.0"; - else if (v.major == 14 && v.minor == 0) arch += "8.0"; - else if (v.major == 13 && v.minor == 10) arch += "7.1"; - else fail << "unable to map msvc compiler version '" << v.string - << "' to runtime version"; - } - - // Use the signature line to generate the checksum. - // - sha256 cs (s); - - return compiler_info { - move (gr.id), - move (v), - move (gr.signature), - cs.string (), - move (arch)}; - } - - compiler_info - guess (const path& cxx, const strings* coptions) - { - string pre (pre_guess (cxx)); - guess_result gr; - - // If we could pre-guess the type based on the excutable name, then - // try the test just for that compiler. - // - if (!pre.empty ()) - { - gr = guess (cxx, pre); - - if (gr.empty ()) - warn << cxx << " name looks like " << pre << " but it is not"; - } - - if (gr.empty ()) - gr = guess (cxx, ""); - - if (gr.empty ()) - fail << "unable to guess C++ compiler type of " << cxx; - - const compiler_id& id (gr.id); - - if (id.type == "gcc") - { - assert (id.variant.empty ()); - return guess_gcc (cxx, coptions, move (gr)); - } - else if (id.type == "clang") - { - assert (id.variant.empty () || id.variant == "apple"); - return guess_clang (cxx, coptions, move (gr)); - } - else if (id.type == "icc") - { - assert (id.variant.empty ()); - return guess_icc (cxx, coptions, move (gr)); - } - else if (id.type == "msvc") - { - assert (id.variant.empty ()); - return guess_msvc (cxx, move (gr)); - } - else - { - assert (false); - return compiler_info (); - } - } - } -} -- cgit v1.1