From 8aeae026d112ff9811a424e31621c05682f4a72e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 18 Apr 2023 09:23:24 +0200 Subject: Add support for Assembler with C Preprocessor (.S) compilation Specifically, the c module now provides the c.as-cpp submodules which can be loaded in order to register the S{} target type and enable Assembler with C Preprocessor compilation in the c compile rule. For details, refer to "Assembler with C Preprocessor Compilation" in the manual. --- libbuild2/cc/common.hxx | 17 ++++++++-- libbuild2/cc/compile-rule.cxx | 73 +++++++++++++++++++++++++------------------ libbuild2/cc/install-rule.cxx | 2 ++ libbuild2/cc/link-rule.cxx | 18 +++++++---- libbuild2/cc/module.cxx | 4 +-- libbuild2/cc/target.cxx | 14 +++++++++ libbuild2/cc/target.hxx | 16 ++++++++++ 7 files changed, 103 insertions(+), 41 deletions(-) (limited to 'libbuild2/cc') diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx index 9090c9c..eefcc0d 100644 --- a/libbuild2/cc/common.hxx +++ b/libbuild2/cc/common.hxx @@ -217,11 +217,14 @@ namespace build2 size_t sys_hdr_dirs_extra; // Note that x_obj is patched in by the x.objx module. So it stays NULL - // if Objective-X compilation is not enabled. + // if Objective-X compilation is not enabled. Similarly for x_asp except + // here we don't have duality and it's purely to signal (by the c.as-cpp + // module) that it's enabled. // const target_type& x_src; // Source target type (c{}, cxx{}). const target_type* x_mod; // Module target type (mxx{}), if any. const target_type* x_obj; // Objective-X target type (m{}, mm{}). + const target_type* x_asp; // Assembler with CPP target type (S{}). // Check if an object (target, prerequisite, etc) is an Objective-X // source. @@ -233,6 +236,16 @@ namespace build2 return x_obj != nullptr && t.is_a (*x_obj); } + // Check if an object (target, prerequisite, etc) is an Assembler with + // C preprocessor source. + // + template + bool + x_assembler_cpp (const T& t) const + { + return x_asp != nullptr && t.is_a (*x_asp); + } + // Array of target types that are considered the X-language headers // (excluding h{} except for C). Keep them in the most likely to appear // order with the "real header" first and terminated with NULL. @@ -305,7 +318,7 @@ namespace build2 sys_lib_dirs_mode (slm), sys_hdr_dirs_mode (shm), sys_mod_dirs_mode (smm), sys_lib_dirs_extra (sle), sys_hdr_dirs_extra (she), - x_src (src), x_mod (mod), x_obj (nullptr), + x_src (src), x_mod (mod), x_obj (nullptr), x_asp (nullptr), x_hdr (hdr), x_inc (inc) {} }; diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index b63b3cf..ca6da03 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -371,13 +371,19 @@ namespace build2 case unit_type::non_modular: case unit_type::module_impl: { - bool obj (x_objective (md.src)); - o1 = "-x"; - switch (x_lang) + + if (x_assembler_cpp (md.src)) + o2 = "assembler-with-cpp"; + else { - case lang::c: o2 = obj ? "objective-c" : "c"; break; - case lang::cxx: o2 = obj ? "objective-c++" : "c++"; break; + bool obj (x_objective (md.src)); + + switch (x_lang) + { + case lang::c: o2 = obj ? "objective-c" : "c"; break; + case lang::cxx: o2 = obj ? "objective-c++" : "c++"; break; + } } break; } @@ -479,7 +485,9 @@ namespace build2 // if (ut == unit_type::module_header ? p.is_a (**x_hdr) || p.is_a () : ut == unit_type::module_intf ? p.is_a (*x_mod) : - p.is_a (x_src) || (x_obj != nullptr && p.is_a (*x_obj))) + p.is_a (x_src) || + (x_asp != nullptr && p.is_a (*x_asp)) || + (x_obj != nullptr && p.is_a (*x_obj))) { // Save in the target's auxiliary storage. // @@ -3021,11 +3029,14 @@ namespace build2 // Preprocessed file extension. // - const char* pext (x_objective (src) ? x_obj_pext : x_pext); + const char* pext (x_assembler_cpp (src) ? ".Si" : + x_objective (src) ? x_obj_pext : + x_pext); // Preprocesor mode that preserves as much information as possible while // still performing inclusions. Also serves as a flag indicating whether - // this compiler uses the separate preprocess and compile setup. + // this (non-MSVC) compiler uses the separate preprocess and compile + // setup. // const char* pp (nullptr); @@ -3036,7 +3047,16 @@ namespace build2 // -fdirectives-only is available since GCC 4.3.0. // if (cmaj > 4 || (cmaj == 4 && cmin >= 3)) - pp = "-fdirectives-only"; + { + // Note that for assembler-with-cpp GCC currently forces full + // preprocessing in (what appears to be) an attempt to paper over + // a deeper issue (see GCC bug 109534). If/when that bug gets + // fixed, we can enable this on our side. Note also that Clang's + // -frewrite-includes appear to work correctly on such files. + // + if (!x_assembler_cpp (src)) + pp = "-fdirectives-only"; + } break; } @@ -6877,7 +6897,6 @@ namespace build2 small_vector module_args; // Module options storage. size_t out_i (0); // Index of the -o option. - //size_t lang_n (0); // Number of lang options. @@ TMP switch (cclass) { @@ -7236,7 +7255,7 @@ namespace build2 args.push_back ("-c"); } - /*lang_n = */append_lang_options (args, md); // @@ TMP + append_lang_options (args, md); if (md.pp == preprocessed::all) { @@ -7288,7 +7307,13 @@ namespace build2 // @@ TODO: why don't we print env (here and/or below)? Also link rule. // if (verb == 1) - print_diag (x_objective (s) ? x_obj_name : x_name, s, t); + { + const char* name (x_assembler_cpp (s) ? "as-cpp" : + x_objective (s) ? x_obj_name : + x_name); + + print_diag (name, s, t); + } else if (verb == 2) print_process (args); @@ -7314,31 +7339,15 @@ namespace build2 { case compiler_type::gcc: { - // @@ TMP -#if 0 - // The -fpreprocessed is implied by .i/.ii. But not when compiling - // a header unit (there is no .hi/.hii). - // - if (ut == unit_type::module_header) - args.push_back ("-fpreprocessed"); - else - // Pop -x since it takes precedence over the extension. - // - // @@ I wonder why bother and not just add -fpreprocessed? Are - // we trying to save an option or does something break? - // - for (; lang_n != 0; --lang_n) - args.pop_back (); -#else // -fpreprocessed is implied by .i/.ii unless compiling a header // unit (there is no .hi/.hii). Also, we would need to pop -x // since it takes precedence over the extension, which would mess // up our osrc logic. So in the end it feels like always passing // explicit -fpreprocessed is the way to go. // + // Also note that similarly there is no .Si for .S files. + // args.push_back ("-fpreprocessed"); -#endif - args.push_back ("-fdirectives-only"); break; } @@ -7518,7 +7527,9 @@ namespace build2 // Preprocessed file extension. // - const char* pext (x_objective (srct) ? x_obj_pext : x_pext); + const char* pext (x_assembler_cpp (srct) ? ".Si" : + x_objective (srct) ? x_obj_pext : + x_pext); // Compressed preprocessed file extension. // diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx index b867466..dae65db 100644 --- a/libbuild2/cc/install-rule.cxx +++ b/libbuild2/cc/install-rule.cxx @@ -91,6 +91,7 @@ namespace build2 return (x_header (p) || p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod)) || + (x_asp != nullptr && p.is_a (*x_asp)) || (x_obj != nullptr && p.is_a (*x_obj))); }; @@ -340,6 +341,7 @@ namespace build2 return (x_header (p) || p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod)) || + (x_asp != nullptr && p.is_a (*x_asp)) || (x_obj != nullptr && p.is_a (*x_obj))); }; diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 76fd3c5..e2bdf5d 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -292,6 +292,7 @@ namespace build2 if (p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod)) || + (x_asp != nullptr && p.is_a (*x_asp)) || (x_obj != nullptr && p.is_a (*x_obj)) || // Header-only X library (or library with C source and X header). (library && x_header (p, false /* c_hdr */))) @@ -1003,6 +1004,7 @@ namespace build2 if (!um) um = (p.is_a (x_src) || p.is_a () || (x_mod != nullptr && p.is_a (*x_mod)) || + (x_asp != nullptr && p.is_a (*x_asp)) || (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a ())) || x_header (p, true)); #endif @@ -1032,8 +1034,9 @@ namespace build2 bool mod (x_mod != nullptr && p.is_a (*x_mod)); bool hdr (false); - if (mod || - p.is_a (x_src) || p.is_a () || + if (mod || + p.is_a (x_src) || p.is_a () || + (x_asp != nullptr && p.is_a (*x_asp)) || (x_obj != nullptr && (p.is_a (*x_obj) || p.is_a ()))) { binless = binless && (mod ? user_binless : false); @@ -1902,7 +1905,8 @@ namespace build2 // if (mod ? p1.is_a (*x_mod) - : (p1.is_a (x_src) || p1.is_a () || + : (p1.is_a (x_src) || p1.is_a () || + (x_asp != nullptr && p1.is_a (*x_asp)) || (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a ())))) { src = true; @@ -1916,8 +1920,9 @@ namespace build2 p1.is_a () || p1.is_a () || p1.is_a () || p1.is_a () || p1.is_a () || p1.is_a () || - ((mod || - p.is_a (x_src) || + ((mod || + p.is_a (x_src) || + (x_asp != nullptr && p.is_a (*x_asp)) || (x_obj != nullptr && p.is_a (*x_obj))) && x_header (p1)) || ((p.is_a () || (x_obj != nullptr && p.is_a ())) && p1.is_a ())) @@ -2062,7 +2067,8 @@ namespace build2 { if (mod ? p1.is_a (*x_mod) - : (p1.is_a (x_src) || p1.is_a () || + : (p1.is_a (x_src) || p1.is_a () || + (x_asp != nullptr && p1.is_a (*x_asp)) || (x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a ())))) { // Searching our own prerequisite is ok, p1 must already be diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index b337754..f33ddf4 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -979,8 +979,8 @@ namespace build2 { using namespace install; - // Note: not registering x_obj (it's registered seperately by the - // x.objx module). + // Note: not registering x_obj or x_asp (they are registered + // seperately by the respective optional submodules). // rs.insert_target_type (x_src); diff --git a/libbuild2/cc/target.cxx b/libbuild2/cc/target.cxx index d743752..6c5d7c8 100644 --- a/libbuild2/cc/target.cxx +++ b/libbuild2/cc/target.cxx @@ -66,6 +66,20 @@ namespace build2 target_type::flag::none }; + extern const char S_ext_def[] = "S"; + const target_type S::static_type + { + "S", + &cc::static_type, + &target_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + target_type::flag::none + }; + extern const char pc_ext[] = "pc"; // VC14 rejects constexpr. const target_type pc::static_type { diff --git a/libbuild2/cc/target.hxx b/libbuild2/cc/target.hxx index 87df326..a078422 100644 --- a/libbuild2/cc/target.hxx +++ b/libbuild2/cc/target.hxx @@ -84,6 +84,22 @@ namespace build2 static const target_type static_type; }; + // Assembler with C preprocessor source file (the same rationale for + // having it here as for c{} above). + // + class LIBBUILD2_CC_SYMEXPORT S: public cc + { + public: + S (context& c, dir_path d, dir_path o, string n) + : cc (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } + + public: + static const target_type static_type; + }; + // pkg-config file targets. // class LIBBUILD2_CC_SYMEXPORT pc: public file // .pc (common) -- cgit v1.1