6 ms·
The build system doesn't need to become a compiler. It just needs to parse the source files for import statements in order to create a DAG.
by ginko 8y ago
The build system doesn't need to become a compiler. It just needs to parse the source files for import statements in order to create a DAG.
- ZirconiumX 8y agoBut you also need compatibility with #include, so your build system also needs to become a preprocessor.
- ginko 8y agoThe preprocessor is a self-contained binary. The build system can just pipe through it.
- gchpaco 8y agoThe C and I presume C++ standard has been very carefully avoiding the idea of a preprocessor being separate at all. The standard was very carefully worded to prevent that being necessary, because most C compilers do not have a separate preprocessor. It is only Unix heritage compilers that really have one, and even they're not consistent about it.
- ZirconiumX 8y agoI'm curious how you plan to detect dependencies from a file by piping it through a command that removes those dependencies.
- jcelerier 8y ago> The build system doesn't need to become a compiler. It just needs to parse parsing C++ is mostly equivalent to becoming a C++ compiler.
- geezerjay 8y ago> parsing C++ is mostly equivalent to becoming a C++ compiler. It reallt isn't. Parsing a languagr just means validating its correctness wrt a grammar and in the process extract some information. Parsing something is just the first stage and a one of many stages required to map C++ source code to valid binaries.
- tcbrindle 8y agoThe presence of template specialisations and constexpr functions means that the GP is right here; you cannot decide whether an arbitrary piece of C++ is syntactically valid without instantiating templates and/or interpreting constexpr functions. Consider template <int> struct foo { template <int> static int bar(int); }; template <> struct foo<8> { static const int bar = 99; }; constexpr int some_function() { return (int) sizeof(void*); }; Now given the snippet foo<some_function()>::bar<0>(1); then if some_function() returns something other than 8, we use the primary template and foo<N>::bar<0>(1) is a call to a static template member function. But if some_function() does return 8, we use the specialisation and the foo<8>::bar is an int with value 99; so we ask is 99 less than the expression 0>(1) (aka "false", promoted to the int 0). That is, there are two entirely different but valid parses depending on whether we are compiling on a 32- or 64-bit system. Parsing C++ is hard. EDIT: Godbolt example: https://godbolt.org/z/yR3YHW https://godbolt.org/z/yR3YHW
- ginko 8y agoYou only need to parse the "module <module name>" and "import <module name>" statements. No need to parse all of C++ for that. You could probably even do that with a regex.
- galangalalgol 8y agoIt also has to do all the preprocessing to see which import statements get hit. I don't think templates could control at compile time which module to import, at least I hope not.
- sobellian 8y agoParsing C++ is literally undecidable. You can encode arbitrary programs which will emit a syntax error depending on their result. Here's an example of a program which compiles only if the constant N is prime, and otherwise emits a syntax error: https://stackoverflow.com/questions/14589346/is-c-context-free-or-context-sensitive https://stackoverflow.com/questions/14589346/is-c-context-fr....
- Deganta 8y agoThey could just specify that the module/import statements need to be at the top of the file (excluding comments). Most people will do this anyway. Then the build system only needs to parse comments and module statements, which should be fast and easy.
- gpderetta 8y agoThat's exactly how it has been specified.
- tcbrindle 8y agoExcept that, as mentioned in TFA, as currently proposed it's possible to say #if SOME_PREPROCESSOR_JUNK import foo; #else import bar; #endif This has legitimate use cases, say #ifdef WIN32 import mymodule.windows #else import mymodule.posix #endif So in reality build systems will be required to invoke at least the preprocessor to extract dependency information. AFAIK the modules support in the Build2 build system does exactly this, and in fact caches the entire preprocessed file to pass to the compiler proper later.
- gpderetta 8y agoThe module preamble has been explicitly designed to be parseable without requiring a full blown C++ parser.
- humanrebar 8y agoOr do the modules equivalent of 'g++ -MM'. Or is there some reason we wouldn't want that approach?
- jsnell 8y agoThat's the problem: you can't do that. Having the compiler produce header dependency information is possible, since the dependencies are just an optimization. If there's no dependency information available, you can just compile all of the files in an arbitrary order, and you get both the object file and a dep file. And then on further runs you use the old dep files to skip unnecessary recompilations. With modules, you can't compile the files in an arbitrary order: if A uses a module defined in B, B must be compiled first. So you need to have the dependency information available up front even for the first build. And since it needs to be available up front, it can't be generated by the compiler. It must either be produced by the build tool which becomes vastly more complicated, or manually by humans.
- coliveira 8y agoThis is not different from a situation where C++ compilation has a binary dependency on other modules. The best know situation is a static library (.a file). In this case, the project cannot be built if there is a static library missing. With modules, one cannot compile the project with missing modules, so the build system will have to provide this information.