summaryrefslogtreecommitdiff
path: root/cook.py
diff options
context:
space:
mode:
authorAlejandro Sior <aho@sior.be>2022-08-06 21:26:03 +0200
committerAlejandro Sior <aho@sior.be>2022-08-06 21:26:03 +0200
commita448a31cab64ab684a79bb179de7a859f8f9da1a (patch)
treeb6a61cde006eed7a5f81276c4a31dd909a7c057e /cook.py
parent25027ef496681ff6f45386f98f9b81b465659e5b (diff)
cook: add preliminary MSVC C toolchain support
It cannot properly create and link against .DLLs yet, the current implementation of it is a hack (using mkextra). Static libraries, executables as well as cc work in a respectable manner.
Diffstat (limited to 'cook.py')
-rwxr-xr-xcook.py201
1 files changed, 141 insertions, 60 deletions
diff --git a/cook.py b/cook.py
index 631311b..ec3aee0 100755
--- a/cook.py
+++ b/cook.py
@@ -39,11 +39,11 @@ class FileMeta(type):
# If none, return none
if path == None:
return None
-
+
# If already a file, return it
if issubclass(type(path), File):
return path
-
+
# Adjust name to be a path
if type(path) == str:
p = Path(path).resolve()
@@ -51,12 +51,12 @@ class FileMeta(type):
path = p.relative_to(ROOT)
else:
path = p
-
+
idx = str(path) + cls.__name__
-
+
if idx in cls.fdb:
return cls.fdb[idx]
-
+
obj = cls.__new__(cls, path, *rest, **krest)
cls.__init__(obj, path, *rest, **krest)
cls.fdb[idx] = obj
@@ -70,7 +70,7 @@ class File(metaclass = FileMeta):
# the target: this can be
# a path or an abstract name
self.path = path
-
+
def __str__(self):
return str(self.path)
@@ -80,16 +80,16 @@ class File(metaclass = FileMeta):
class Target(File):
def __init__(self, path, processor, inputs, extra_args = []):
super().__init__(path)
-
+
# The generator that created this process
self.processor = processor
-
+
# The inputs
self.inputs = File.resolve(inputs)
-
+
# The extra arguments taken
self.extra_args = extra_args
-
+
def gen_makefile(self):
inputs = " ".join([str(x) for x in self.inputs])
extra_args = " ".join([x for x in self.extra_args])
@@ -97,13 +97,13 @@ class Target(File):
out = "%s: %s\n" % (self.path, inputs)
out += "\t@$(%s) %s\n" % (self.processor.name, self.processor.mkargs("$@", inputs, extra_args))
out += "\t@echo '%s'" % (self.processor.mkdesc("$@", inputs, extra_args))
-
+
return out
-
+
def gen_ninja(self):
inputs = " ".join([str(x) for x in self.inputs])
extra_args = " ".join(self.extra_args)
-
+
out = "build %s: %s %s" % (self.path, self.processor.name, inputs)
if self.extra_args != []:
out += "\n extra = %s" % (extra_args)
@@ -115,10 +115,10 @@ class ProcessorMeta(type):
# If already a file, return it
if issubclass(type(name), Processor):
return name
-
+
if name in cls.mdb:
return cls.mdb[name]
-
+
obj = cls.__new__(cls, name, *rest, **krest)
cls.__init__(obj, name, *rest, **krest)
cls.mdb[name] = obj
@@ -127,29 +127,38 @@ class ProcessorMeta(type):
class Processor(metaclass = ProcessorMeta):
mdb = dict()
- def __init__(self, name, exe, mkargs, mkout=DEFMKOUT, mkdesc=DEFMKDESC):
+ def __init__(self, name, exe, mkargs, mkextra=[], mkout=DEFMKOUT, mkdesc=DEFMKDESC):
# The name of the processor
self.name = name
-
+
# Executable used in the process
self.exe = File(exe)
-
+
# The arguments that is used to launch the process
self.mkargs = wrap_lambda(mkargs)
-
+
+ # A list of functions indicating how to build extra arguments
+ # XXX: TODO: mkextra is a horrible hack
+ self.mkextra = mkextra
+
# The way to generate output path
self.mkout = wrap_lambda(mkout)
-
+
# The description
self.mkdesc = wrap_lambda(mkdesc)
def __call__(self, name, *args, extra_args = []):
return self.gen(name, *args, extra_args = extra_args)
-
+
def gen(self, name, *args, extra_args = []):
inputs = File.resolve(args)
path = BUILD / Path(".").resolve().relative_to(ROOT) / self.mkout(name)
-
+
+ # XXX: consider passing the arrays of files to handlers directly
+ i = " ".join([str(inp) for inp in inputs])
+ for mk in self.mkextra:
+ extra_args += [mk(name)]
+
return Target(path, self, inputs, extra_args)
def gen_makefile(self):
@@ -157,27 +166,32 @@ class Processor(metaclass = ProcessorMeta):
def gen_ninja(self):
out = "rule %s\n" % (self.name)
-
+
cmd = "%s %s" % (self.exe, self.mkargs("$out", "$in", "$extra"))
-
+
out += " command = %s\n" % (cmd)
out += " description = %s\n" % (self.mkdesc("$out", "$in", cmd))
return out
class Vec(Processor):
- def __init__(self, name, exe, mkargs, mkout, mkdesc=DEFMKDESC):
- super().__init__(name, exe, mkargs, mkout = mkout, mkdesc = mkdesc)
-
+ def __init__(self, name, exe, mkargs, mkout, mkextra=[], mkdesc=DEFMKDESC):
+ super().__init__(name, exe, mkargs, mkextra = mkextra, mkout = mkout, mkdesc = mkdesc)
+
def __call__(self, *args, extra_args = []):
return self.gen(*args, extra_args = extra_args)
-
+
def gen(self, *args, extra_args = []):
args = File.resolve(args)
extra_args = flatten(extra_args)
-
+
targets = []
for i in args:
path = BUILD / i.path.parent / self.mkout(i.path)
+ extra_p = extra_args
+
+ # XXX: consider passing the i File directly to mk
+ for mk in self.mkextra:
+ extra_p += [mk(path)]
targets.append(Target(path, self, [i], extra_args))
@@ -187,74 +201,88 @@ class CTarget(Target):
def __init__(self, path, processor, inputs, header_depends, extra_args = []):
super().__init__(path, processor, inputs, extra_args = extra_args)
self.header_depends = File.resolve(header_depends)
-
+
def gen_makefile(self):
out = []
if self.header_depends != []:
out += ["-include %s\n" % hdep for hdep in self.header_depends]
-
+
out += super().gen_makefile()
return "".join(out)
class CC(Vec):
- def __init__(self, name, exe, mkargs, mkout, mkinc, mkdep=None, depstyle=None, mkdesc=DEFMKDESC):
- super().__init__(name, exe, mkargs, mkout, mkdesc=mkdesc)
+ def __init__(self, name, exe, mkargs, mkout, mkinc, mkextra=[], mkdep=None, depstyle=None, mkdesc=DEFMKDESC):
+ super().__init__(name, exe, mkargs, mkout, mkextra=mkextra, mkdesc=mkdesc)
self.mkinc = mkinc
self.mkdep = mkdep
self.depstyle = depstyle
-
+
def __call__(self, *args, extra_args = [], include_dirs = []):
return self.gen(*args, extra_args = extra_args, include_dirs = include_dirs)
-
+
def gen(self, *args, extra_args = [], include_dirs = []):
inputs = File.resolve(args)
-
+
extra_args = flatten(extra_args)
include_dirs = File.resolve(include_dirs)
include_args = [self.mkinc(str(x)) for x in include_dirs]
extra_args += include_args
-
+
targets = []
for input in inputs:
out = self.mkout(input.path)
path = BUILD / out
- hdep = BUILD / self.mkdep(out)
- targets.append(CTarget(path, self, input, header_depends = hdep, extra_args = extra_args))
+ header_depends = []
+ if self.mkdep:
+ hdep += BUILD / self.mkdep(out)
+
+ extra_p = extra_args
+ # XXX: consider passing the i File directly to mk
+ for mk in self.mkextra:
+ extra_p += [mk(path)]
+
+ targets.append(CTarget(path, self, input, header_depends = header_depends, extra_args = extra_p))
return targets
-
+
def gen_ninja(self):
out = super().gen_ninja()
-
- if self.mkdep and self.depstyle == "gcc":
+
+ if self.depstyle == "gcc":
out += " deps = gcc\n"
+ elif self.depstyle == "msvc":
+ out += " deps = msvc\n"
+ else:
+ raise Exception("ninja dependencies for selected depstyle is unimplemented")
+
+ if self.mkdep and self.depstyle == "gcc":
out += " depfile = %s\n" % (self.mkdep("$out"))
elif self.mkdep:
raise Exception("ninja dependencies for selected depstyle is unimplemented")
-
+
return out
class CExe(Processor):
- def __init__(self, name, exe, mkargs, mklib, mkout = DEFMKOUT, mkdesc=DEFMKDESC):
- super().__init__(name, exe, mkargs, mkout = mkout, mkdesc = mkdesc)
-
+ def __init__(self, name, exe, mkargs, mklib, mkextra = [], mkout = DEFMKOUT, mkdesc=DEFMKDESC):
+ super().__init__(name, exe, mkargs, mkextra = mkextra, mkout = mkout, mkdesc = mkdesc)
+
self.mklib = mklib
-
+
def __call__(self, name, *args, libs = [], extra_args = []):
return self.gen(name, *args, libs = libs, extra_args = extra_args)
-
+
def gen(self, name, *args, libs, extra_args = []):
extra_args = flatten(extra_args)
libs = flatten(libs)
-
+
# Note: this is bad, perhaps take a "depends" also/instead
# XXX Handling of libraries is temporary
libs_args = [self.mklib(lib) for lib in libs]
extra_args += libs_args
-
+
return super().gen(name, *args, extra_args = extra_args)
def CCStyleToolchain(name, gccname, arname, suffix="", c_args=[], ld_args=[]):
@@ -262,10 +290,10 @@ def CCStyleToolchain(name, gccname, arname, suffix="", c_args=[], ld_args=[]):
exename = name + "exe" + suffix
libname = name + "lib" + suffix
shlibname = name + "shlib" + suffix
-
+
c_args = " ".join(flatten(c_args))
ld_args = " ".join(flatten(ld_args))
-
+
gcc = find_program(gccname)
ar = find_program(arname)
@@ -313,7 +341,59 @@ def ClangToolchain(suffix="", c_args=[], ld_args=[]):
def CCToolchain(suffix="", c_args=[], ld_args=[]):
return CCStyleToolchain("cc", "cc", "ar", suffix, c_args, ld_args)
+def MSVCToolchain(suffix="", c_args=[], ld_args=[]):
+ ccname = "msvc" + suffix
+ exename = "msvcexe" + suffix
+ libname = "msvclib" + suffix
+ shlibname = "msvcshlib" + suffix
+
+ c_args = " ".join(flatten(c_args))
+ ld_args = " ".join(flatten(ld_args))
+
+ cl = find_program("cl")
+ lib = find_program("lib")
+ link = find_program("link")
+
+ cc = CC(
+ name=ccname,
+ exe=cl,
+ mkargs=lambda o, i, ea: "/showIncludes /nologo /D_USRDLL /D_WINDLL /Fo%s /c %s /MT %s %s" % (o, i, c_args, ea),
+ mkout=lambda i: "%s.obj" % (i),
+ mkinc=lambda inc: "/I%s" % (inc),
+ mkdesc=lambda o, i, cmd: "CC %s" % (o),
+ depstyle="msvc"
+ )
+ exe = CExe(
+ name=exename,
+ exe=link,
+ mkargs=lambda o, i, ea: "/OUT:%s %s %s %s" % (o, i, ld_args, ea),
+ mklib=lambda l: l,
+ mkdesc=lambda o, i, cmd: "LD %s" % (o),
+ mkout=lambda n: "%s.exe" % (n),
+ )
+ lib = CExe(
+ name=libname,
+ exe=lib,
+ mkargs=lambda o, i, ea: "/OUT:%s %s %s" % (o, i, ea),
+ mklib=lambda l: "", # TODO: this should not be needed
+ mkdesc=lambda o, i, cmd: "AR %s" % (o),
+ mkout=lambda n: "lib%s.lib" % (n)
+ )
+ shlib = CExe(
+ name=shlibname,
+ exe=cl,
+ mkargs=lambda o, i, ea: "/D_USRDLL /D_WINDLL %s /MT /link /DLL %s %s" % (i, ld_args, ea),
+ mklib=lambda l: "", # TODO: make this not needed
+ mkdesc=lambda o, i, cmd: "SO %s" % (o),
+ mkout=lambda n: "%s.lib" % (n),
+ # XXX: TODO: mkextra is very bad, do this properly
+ # perhaps, by specializing CTarget or injecting data into it
+ mkextra=[lambda n: "/OUT:" + str(BUILD / CWD / ("%s.dll" % (n)))]
+ )
+ return cc, exe, lib, shlib
+
CTOOLCHAINS = {
+ 'msvc': MSVCToolchain,
'cc': CCToolchain,
'clang': ClangToolchain,
'gcc': GccToolchain
@@ -328,7 +408,7 @@ def CToolchain(name="system", suffix="", c_args=[], ld_args=[]):
if name != "system":
if not name in CTOOLCHAINS:
raise Exception("No such C toolchain available")
-
+
return CTOOLCHAINS[name](suffix, c_args, ld_args)
for t in CTOOLCHAINS.values():
@@ -336,7 +416,7 @@ def CToolchain(name="system", suffix="", c_args=[], ld_args=[]):
return t(suffix, c_args, ld_args)
except:
pass
-
+
raise Exception("No C toolchain found on system")
global cc, exe, lib, shlib
@@ -352,15 +432,16 @@ def find_one(names):
def subdir(dir):
# Save the previous location
PWD = Path.cwd()
-
+
# Go into subdir
sub = PWD / dir
os.chdir(sub)
-
+
# Set helper path
+ global CWD
CWD = Path.cwd().relative_to(ROOT)
exec(open('.build').read())
-
+
# Revert the current directory;
os.chdir(PWD)
@@ -374,13 +455,13 @@ def gen_bld():
def gen_makefile():
out = [mach.gen_makefile() + "\n" for mach in Processor.mdb.values()]
out += ["\n\n" + target.gen_makefile() for target in File.fdb.values() if issubclass(type(target), Target)]
-
+
return "".join(out)
-
+
def gen_ninja():
out = [mach.gen_ninja() + "\n" for mach in Processor.mdb.values()]
out += [target.gen_ninja() + "\n" for target in File.fdb.values() if issubclass(type(target), Target)]
-
+
return "".join(out)
parser = argparse.ArgumentParser(description="cook build system")