aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-04-18 09:23:24 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-04-18 09:23:24 +0200
commit8aeae026d112ff9811a424e31621c05682f4a72e (patch)
tree9dc5c736fb07fb6cafe3a08a7e999b9c084edc18
parenta1a01cbce316c5da7aa94e6b6a71eae98bffed1c (diff)
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.
-rw-r--r--NEWS7
-rw-r--r--doc/manual.cli84
-rw-r--r--libbuild2/c/init.cxx41
-rw-r--r--libbuild2/c/init.hxx4
-rw-r--r--libbuild2/c/target.hxx1
-rw-r--r--libbuild2/cc/common.hxx17
-rw-r--r--libbuild2/cc/compile-rule.cxx73
-rw-r--r--libbuild2/cc/install-rule.cxx2
-rw-r--r--libbuild2/cc/link-rule.cxx18
-rw-r--r--libbuild2/cc/module.cxx4
-rw-r--r--libbuild2/cc/target.cxx14
-rw-r--r--libbuild2/cc/target.hxx16
-rw-r--r--libbuild2/cxx/init.cxx4
-rw-r--r--libbuild2/module.hxx6
14 files changed, 249 insertions, 42 deletions
diff --git a/NEWS b/NEWS
index 6a7cbcd..2bfb0e7 100644
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,13 @@ Version 0.16.0
and no attempt to automatically link any necessary Objective-C runtime
(such as -lobjc) is made.
+ * 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.
+
* Low verbosity diagnostics rework.
The low verbosity (level 1) rule diagnostics format has been adjusted to
diff --git a/doc/manual.cli b/doc/manual.cli
index 7f6b730..6f3def4 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -7329,6 +7329,90 @@ automatically link any necessary Objective-C runtime library (such as
\c{-lobjc}).
+\h#c-as-cpp|Assembler with C Preprocessor Compilation|
+
+The \c{c} module provides the \c{c.as-cpp} submodules which can be loaded in
+order to register the \c{S{\}} target type and enable Assembler with C
+Preprocessor compilation in the \c{C} compile rule. Note that \c{c.as-cpp}
+must be loaded after the \c{c} module and while the \c{S{\}} target type is
+registered unconditionally, compilation is only enabled if the C compiler
+supports Assembler with C Preprocessor compilation.
+
+Typical usage:
+
+\
+# root.build
+#
+using c
+using c.as-cpp
+\
+
+\
+# buildfile
+#
+exe{hello}: {h c}{* -hello.c}
+
+# Use C implementation as a fallback if no assembler.
+#
+assembler = ($c.class == 'gcc' && $c.target.cpu == 'x86_64')
+
+exe{hello}: S{hello}: include = $assembler
+exe{hello}: c{hello}: include = (!$assembler)
+\
+
+\
+/* hello.S
+ */
+#ifndef HELLO_RESULT
+# define HELLO_RESULT 0
+#endif
+
+text
+
+.global hello
+hello:
+ /* ... */
+ movq $HELLO_RESULT, %rax
+ ret
+
+#ifdef __ELF__
+.section .note.GNU-stack, \"\", @progbits
+#endif
+\
+
+The default file extension for the \c{S{\}} target type is \c{.S} (capital)
+but that can be customized using the standard mechanisms. For example:
+
+\
+# root.build
+#
+using c
+using c.as-cpp
+
+h{*}: extension = h
+c{*}: extension = c
+S{*}: extension = sx
+\
+
+Note that \c{*.coptions} are passed to the C compiler when compiling Assembler
+with C Preprocessor files because compile options may cause additional
+preprocessor macros to be defined. Plus, some of them (such as \c{-g}) are
+passed (potentially translated) to the underlying assembler. To pass
+additional options when compiling Assembler files use \c{c.poptions} and
+\c{c.coptions}. For example (continuing with the previous example):
+
+\
+if $assembler
+{
+ obj{hello}:
+ {
+ c.poptions += -DHELLO_RESULT=1
+ c.coptions += -Wa,--no-pad-sections
+ }
+}
+\
+
+
\h1#module-cxx|\c{cxx} Module|
\N{This chapter is a work in progress and is incomplete.}
diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx
index 2dbd534..0092b81 100644
--- a/libbuild2/c/init.cxx
+++ b/libbuild2/c/init.cxx
@@ -324,11 +324,15 @@ namespace build2
nullptr
};
+ // Note that we include S{} here because .S files can include each other.
+ // (And maybe from inline assember instrcutions?)
+ //
static const target_type* const inc[] =
{
&h::static_type,
&c::static_type,
&m::static_type,
+ &S::static_type,
nullptr
};
@@ -446,6 +450,42 @@ namespace build2
return true;
}
+ bool
+ as_cpp_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ bool,
+ bool,
+ module_init_extra&)
+ {
+ tracer trace ("c::as_cpp_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // We only support root loading (which means there can only be one).
+ //
+ if (rs != bs)
+ fail (loc) << "c.as-cpp module must be loaded in project root";
+
+ module* mod (rs.find_module<module> ("c"));
+
+ if (mod == nullptr)
+ fail (loc) << "c.as-cpp module must be loaded after c module";
+
+ // Register the target type and "enable" it in the module.
+ //
+ // Note that we must register the target type regardless of whether the
+ // C compiler is capable of compiling Assember with C preprocessor. But
+ // we enable only if it is.
+ //
+ rs.insert_target_type<S> ();
+
+ if (mod->ctype == compiler_type::gcc ||
+ mod->ctype == compiler_type::clang)
+ mod->x_asp = &S::static_type;
+
+ return true;
+ }
+
static const module_functions mod_functions[] =
{
// NOTE: don't forget to also update the documentation in init.hxx if
@@ -455,6 +495,7 @@ namespace build2
{"c.config", nullptr, config_init},
{"c", nullptr, init},
{"c.objc", nullptr, objc_init},
+ {"c.as-cpp", nullptr, as_cpp_init},
{nullptr, nullptr, nullptr}
};
diff --git a/libbuild2/c/init.hxx b/libbuild2/c/init.hxx
index f324c31..c3126ea 100644
--- a/libbuild2/c/init.hxx
+++ b/libbuild2/c/init.hxx
@@ -23,7 +23,9 @@ namespace build2
// `c.config` -- loads c.guess and sets more variables.
// `c` -- loads c.config and registers target types and rules.
// `c.objc` -- registers m{} target type and enables Objective-C
- // compilation.
+ // compilation. Must be loaded after c.
+ // `c.as-cpp` -- registers S{} target type and enables Assembler with
+ // C preprocessor compilation. Must be loaded after c.
//
extern "C" LIBBUILD2_C_SYMEXPORT const module_functions*
build2_c_load ();
diff --git a/libbuild2/c/target.hxx b/libbuild2/c/target.hxx
index 308bda9..39fcf89 100644
--- a/libbuild2/c/target.hxx
+++ b/libbuild2/c/target.hxx
@@ -16,6 +16,7 @@ namespace build2
using cc::h;
using cc::c;
using cc::m;
+ using cc::S;
}
}
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 <typename T>
+ 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<h> () :
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<string, 2> 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<c> () ||
(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<m> ())) ||
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<c> () ||
+ if (mod ||
+ p.is_a (x_src) || p.is_a<c> () ||
+ (x_asp != nullptr && p.is_a (*x_asp)) ||
(x_obj != nullptr && (p.is_a (*x_obj) || p.is_a<m> ())))
{
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<c> () ||
+ : (p1.is_a (x_src) || p1.is_a<c> () ||
+ (x_asp != nullptr && p1.is_a (*x_asp)) ||
(x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a<m> ()))))
{
src = true;
@@ -1916,8 +1920,9 @@ namespace build2
p1.is_a<libx> () ||
p1.is_a<liba> () || p1.is_a<libs> () || p1.is_a<libux> () ||
p1.is_a<bmi> () || p1.is_a<bmix> () ||
- ((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<c> () ||
(x_obj != nullptr && p.is_a<m> ())) && p1.is_a<h> ()))
@@ -2062,7 +2067,8 @@ namespace build2
{
if (mod
? p1.is_a (*x_mod)
- : (p1.is_a (x_src) || p1.is_a<c> () ||
+ : (p1.is_a (x_src) || p1.is_a<c> () ||
+ (x_asp != nullptr && p1.is_a (*x_asp)) ||
(x_obj != nullptr && (p1.is_a (*x_obj) || p1.is_a<m> ()))))
{
// 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<S>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<S_ext_def>,
+ &target_pattern_var<S_ext_def>,
+ 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)
diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx
index fd6d04c..732b08b 100644
--- a/libbuild2/cxx/init.cxx
+++ b/libbuild2/cxx/init.cxx
@@ -783,6 +783,10 @@ namespace build2
nullptr
};
+ // Note that we don't include S{} here because none of the files we
+ // compile can plausibly want to include .S. (Maybe in inline assember
+ // instrcutions?)
+ //
static const target_type* const inc[] =
{
&hxx::static_type,
diff --git a/libbuild2/module.hxx b/libbuild2/module.hxx
index 8223bae..2f2d8a7 100644
--- a/libbuild2/module.hxx
+++ b/libbuild2/module.hxx
@@ -21,6 +21,12 @@ namespace build2
// implementation's perspectives, the module library is "loaded" and the
// module is optionally "bootstrapped" (or "booted" for short) and then
// "initialized" (or "inited").
+ //
+ // Note also that a module name (or component thereof, for submodules) is
+ // not a project name (in particular, it can be less than 3 characters long)
+ // and we usually use `-` instead of `_` as a word separator within
+ // components, for example `c.as-cpp` (since the top-level component ends up
+ // in the library name; but this is not a hard rule).
// Base class for module instance.
//