[build] Move headers out of target dirs

The great header shift: It didn't make sense to regenerate headers for
the same module for every target (boot/kernel/user) it appeared in. And
now that core headers are out of src/include, this was going to cause
problems for the new libc changes I've been working on. So I went back
to re-design how module headers work.

Pre-requisites:
- A module's public headers should all be available in one location, not
  tied to target.
- No accidental includes. Another module should not be able to include
  anything (creating an implicit dependency) from a module without
  declaring an explicit dependency.
- Exception to the previous: libc's headers should be available to all,
  at least for the freestanding headers.

New system:
- A new "public_headers" property of module declares all public headers
  that should be available to dependant modules
- All public headers (after possible processing) are installed relative
  to build/include/<module> with the same path as their source
- This also means no "include" dir in modules is necessary. If a header
  should be included as <j6/types.h> then its source should be
  src/libraries/j6/j6/types.h - this caused the most churn as all public
  header sources moved one directory up.
- The "includes" property of a module is local only to that module now,
  it does not create any implicit public interface

Other changes:
- The bonnibel concept of sources changed: instead of sources having
  actions, they themselves are an instance of a (sub)class of Source,
  which provides all the necessary information itself.
- Along with the above, rule names were standardized into <type>.<ext>,
  eg "compile.cpp" or "parse.cog"
- cog and cogflags variables moved from per-target scope to global scope
  in the build files.
- libc gained a more dynamic .module file
This commit is contained in:
Justin C. Miller
2022-02-06 10:18:51 -08:00
parent db23e4966e
commit 4545256b49
103 changed files with 362 additions and 5191 deletions

View File

@@ -1,87 +1,119 @@
class Action:
name = property(lambda self: self.__name)
implicit = property(lambda self: False)
rule = property(lambda self: None)
deps = property(lambda self: tuple())
parse_deps = property(lambda self: False)
from os.path import join, splitext
from . import mod_rel
def __init__(self, name):
self.__name = name
def output_of(self, path):
return None
class Compile(Action):
rule = property(lambda self: f'compile_{self.name}')
deps = property(lambda self: ("${module_dir}/.parse_dep.phony",))
parse_deps = property(lambda self: True)
def __init__(self, name, suffix = ".o"):
super().__init__(name)
self.__suffix = suffix
def output_of(self, path):
return str(path) + self.__suffix
class Parse(Action):
rule = property(lambda self: f'parse_{self.name}')
def output_of(self, path):
suffix = "." + self.name
if path.suffix == suffix:
return path.with_suffix('')
def _resolve(path):
if path.startswith('/') or path.startswith('$'):
return path
from pathlib import Path
return str(Path(path).resolve())
class Link(Action): pass
class Header(Action):
implicit = property(lambda self: True)
def _dynamic_action(name):
def prop(self):
root, suffix = splitext(self.path)
return f"{name}{suffix}"
return prop
class Source:
Actions = {
'.c': Compile('c'),
'.cpp': Compile('cxx'),
'.s': Compile('asm'),
'.cog': Parse('cog'),
'.o': Link('o'),
'.h': Header('h'),
'.inc': Header('inc'),
}
next = tuple()
action = None
args = dict()
gather = False
outputs = tuple()
input = False
def __init__(self, root, path, output=None, deps=tuple()):
from pathlib import Path
self.__root = Path(root)
self.__path = Path(path)
self.__output = output
self.__deps = tuple(deps)
def __str__(self):
return self.input
def __init__(self, path, root = "${module_dir}", deps=tuple()):
self.path = path
self.root = root
self.deps = deps
def add_deps(self, deps):
self.__deps += tuple(deps)
self.deps += tuple(deps)
@property
def action(self):
suffix = self.__path.suffix
return self.Actions.get(suffix)
def fullpath(self):
return join(self.root, self.path)
class ParseSource(Source):
action = property(_dynamic_action("parse"))
@property
def output(self):
if not self.action:
return None
root, _ = splitext(self.path)
return root
path = self.__output
if path is None:
path = self.action.output_of(self.__path)
@property
def outputs(self):
return (self.output,)
return path and Source("${module_dir}", path)
@property
def gather(self):
_, suffix = splitext(self.output)
return suffix in (".h", ".inc")
deps = property(lambda self: self.__deps)
name = property(lambda self: str(self.__path))
input = property(lambda self: str(self.__root / self.__path))
@property
def next(self):
_, suffix = splitext(self.output)
nextType = {
".s": CompileSource,
".cpp": CompileSource,
}.get(suffix)
if nextType:
return (nextType(self.output),)
return tuple()
@property
def args(self):
return dict(
outputs = list(map(mod_rel, self.outputs)),
inputs = [self.fullpath],
implicit = list(map(_resolve, self.deps)),
variables = dict(name=self.path),
)
class HeaderSource(Source):
action = "cp"
gather = True
@property
def outputs(self):
return (self.path,)
@property
def args(self):
return dict(
outputs = [mod_rel(self.path)],
inputs = [join(self.root, self.path)],
implicit = list(map(_resolve, self.deps)),
variables = dict(name=self.path),
)
class CompileSource(Source):
action = property(_dynamic_action("compile"))
input = True
@property
def outputs(self):
return (self.path + ".o",)
@property
def args(self):
return dict(
outputs = list(map(mod_rel, self.outputs)),
inputs = [join(self.root, self.path)],
implicit = list(map(_resolve, self.deps)) + [mod_rel(".headers.phony")],
variables = dict(name=self.path),
)
def make_source(root, path):
_, suffix = splitext(path)
if suffix in (".s", ".c", ".cpp"):
return CompileSource(path, root)
elif suffix in (".cog",):
return ParseSource(path, root)
elif suffix in (".h", ".inc"):
return HeaderSource(path, root)
else:
raise RuntimeError(f"{path} has no Source type")