Extracted build data to its own class.

This commit is contained in:
Jussi Pakkanen 2013-01-11 22:59:49 +02:00
parent 36e2b0cd37
commit c71f82432f
4 changed files with 70 additions and 45 deletions

37
build.py Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/python3 -tt
# Copyright 2012 Jussi Pakkanen
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class Build:
"""A class that holds the status of one build including
all dependencies and so on.
"""
def __init__(self, environment):
self.environment = environment
self.project = None
self.targets = {}
self.compilers = []
self.tests = []
self.static_linker = self.environment.detect_static_linker()
def get_project(self):
return self.project
def get_targets(self):
return self.targets
def get_tests(self):
return self.tests

View File

@ -18,7 +18,7 @@ from optparse import OptionParser
import sys, stat
import os.path
import environment, interpreter
import shellgenerator
import shellgenerator, build
parser = OptionParser()
@ -27,7 +27,7 @@ parser.add_option('--libdir', default='lib', dest='libdir')
parser.add_option('--includedir', default='include', dest='includedir')
parser.add_option('--datadir', default='share', dest='datadir')
class Builder():
class BuilderApp():
builder_filename = 'builder.txt'
def __init__(self, dir1, dir2, options):
@ -35,7 +35,7 @@ class Builder():
self.options = options
def has_builder_file(self, dirname):
fname = os.path.join(dirname, Builder.builder_filename)
fname = os.path.join(dirname, BuilderApp.builder_filename)
try:
ifile = open(fname, 'r')
ifile.close()
@ -55,20 +55,22 @@ class Builder():
raise RuntimeError('Source and build directories must not be the same. Create a pristine build directory.')
if self.has_builder_file(ndir1):
if self.has_builder_file(ndir2):
raise RuntimeError('Both directories contain a builder file %s.' % Builder.builder_filename)
raise RuntimeError('Both directories contain a builder file %s.' % BuilderApp.builder_filename)
return (ndir1, ndir2)
if self.has_builder_file(ndir2):
return (ndir2, ndir1)
raise RuntimeError('Neither directory contains a builder file %s.' % Builder.builder_filename)
raise RuntimeError('Neither directory contains a builder file %s.' % BuilderApp.builder_filename)
def generate(self):
code = open(os.path.join(self.source_dir, Builder.builder_filename)).read()
code = open(os.path.join(self.source_dir, BuilderApp.builder_filename)).read()
if len(code.strip()) == 0:
raise interpreter.InvalidCode('Builder file is empty.')
assert(isinstance(code, str))
env = environment.Environment(self.source_dir, self.build_dir)
intr = interpreter.Interpreter(code, env)
g = shellgenerator.ShellGenerator(intr, env)
b = build.Build(env)
intr = interpreter.Interpreter(code, b)
intr.run()
g = shellgenerator.ShellGenerator(b)
g.generate()
if __name__ == '__main__':
@ -81,7 +83,7 @@ if __name__ == '__main__':
dir2 = args[2]
else:
dir2 = '.'
builder = Builder(dir1, dir2, options)
builder = BuilderApp(dir1, dir2, options)
print ('Source dir: ' + builder.source_dir)
print ('Build dir: ' + builder.build_dir)
builder.generate()

View File

@ -116,17 +116,13 @@ class Test(InterpreterObject):
class Interpreter():
def __init__(self, code, environment):
def __init__(self, code, build):
self.build = build
self.ast = parser.build_ast(code)
self.sanity_check_ast()
self.project = None
self.compilers = []
self.targets = {}
self.variables = {}
self.environment = environment
self.static_linker = self.environment.detect_static_linker()
self.environment = build.environment
self.build_func_dict()
self.tests = []
def build_func_dict(self):
self.funcs = {'project' : self.func_project,
@ -139,15 +135,6 @@ class Interpreter():
'add_test' : self.func_add_test
}
def get_project(self):
return self.project
def get_targets(self):
return self.targets
def get_tests(self):
return self.tests
def sanity_check_ast(self):
if not isinstance(self.ast, nodes.CodeBlock):
raise InvalidCode('AST is of invalid type. Possibly a bug in the parser.')
@ -189,10 +176,10 @@ class Interpreter():
def func_project(self, node, args):
self.validate_arguments(args, 1, [str])
if self.project is not None:
if self.build.project is not None:
raise InvalidCode('Second call to project() on line %d.' % node.lineno())
self.project = args[0]
print('Project name is "%s".' % self.project)
self.build.project = args[0]
print('Project name is "%s".' % self.build.project)
def func_message(self, node, args):
self.validate_arguments(args, 1, [str])
@ -204,17 +191,17 @@ class Interpreter():
for a in args:
if not isinstance(a, str):
raise InvalidArguments('Line %d: Argument %s is not a string.' % (node.lineno(), str(a)))
if len(self.compilers) > 0:
if len(self.build.compilers) > 0:
raise InvalidCode('Function language() can only be called once (line %d).' % node.lineno())
for lang in args:
if lang.lower() == 'c':
comp = self.environment.detect_c_compiler()
comp.sanity_check(self.environment.get_scratch_dir())
self.compilers.append(comp)
self.build.compilers.append(comp)
elif lang.lower() == 'c++':
comp = self.environment.detect_cxx_compiler()
comp.sanity_check(self.environment.get_scratch_dir())
self.compilers.append(comp)
self.build.compilers.append(comp)
else:
raise InvalidCode('Tried to use unknown language "%s".' % lang)
@ -236,7 +223,7 @@ class Interpreter():
def func_add_test(self, node, args):
self.validate_arguments(args, 2, [str, Executable])
t = Test(args[0], args[1])
self.tests.append(t)
self.build.tests.append(t)
print('Adding test "%s"' % args[0])
def build_target(self, node, args, targetclass):
@ -245,10 +232,10 @@ class Interpreter():
raise InvalidArguments('Line %d: Argument %s is not a string.' % (node.lineno(), str(a)))
name= args[0]
sources = args[1:]
if name in self.targets:
if name in self.build.targets:
raise InvalidCode('Line %d: tried to create target "%s", but a target of that name already exists.' % (node.lineno(), name))
l = targetclass(name, sources, self.environment)
self.targets[name] = l
self.build.targets[name] = l
print('Creating build target "%s" with %d files.' % (name, len(sources)))
return l

View File

@ -22,15 +22,14 @@ def shell_quote(cmdlist):
class ShellGenerator():
def __init__(self, interpreter, environment):
self.environment = environment
self.interpreter = interpreter
def __init__(self, build):
self.build = build
self.environment = build.environment
self.build_filename = 'compile.sh'
self.test_filename = 'run_tests.sh'
self.processed_targets = {}
def generate(self):
self.interpreter.run()
self.generate_compile_script()
self.generate_test_script()
@ -39,7 +38,7 @@ class ShellGenerator():
outfile = open(outfilename, 'w')
outfile.write('#!/bin/sh\n\n')
outfile.write('echo This is an autogenerated shell script build file for project \\"%s\\".\n'
% self.interpreter.get_project())
% self.build.get_project())
outfile.write('echo This is experimental and most likely will not work!\n')
cdcmd = ['cd', self.environment.get_build_dir()]
outfile.write(' '.join(shell_quote(cdcmd)) + '\n')
@ -53,7 +52,7 @@ class ShellGenerator():
outfile = open(outfilename, 'w')
outfile.write('#!/bin/sh\n\n')
outfile.write('echo This is an autogenerated shell script test file for project \\"%s\\".\n'
% self.interpreter.get_project())
% self.build.get_project())
outfile.write('echo Run the compile script before this one or bad things will happen!\n')
cdcmd = ['cd', self.environment.get_build_dir()]
outfile.write(' '.join(shell_quote(cdcmd)) + '\n')
@ -63,7 +62,7 @@ class ShellGenerator():
stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
def generate_tests(self, outfile):
for t in self.interpreter.get_tests():
for t in self.build.get_tests():
cmds = []
cmds.append(self.get_target_filename(t.get_exe()))
outfile.write('echo Running test \\"%s\\".\n' % t.get_name())
@ -71,7 +70,7 @@ class ShellGenerator():
def generate_single_compile(self, target, outfile, src):
compiler = None
for i in self.interpreter.compilers:
for i in self.build.compilers:
if i.can_compile(src):
compiler = i
break
@ -108,9 +107,9 @@ class ShellGenerator():
def generate_link(self, target, outfile, outname, obj_list):
if isinstance(target, interpreter.StaticLibrary):
linker = self.interpreter.static_linker
linker = self.build.static_linker
else:
linker = self.interpreter.compilers[0] # Fixme.
linker = self.build.compilers[0] # Fixme.
commands = []
commands += linker.get_exelist()
if isinstance(target, interpreter.Executable):
@ -138,7 +137,7 @@ class ShellGenerator():
return dirname
def generate_commands(self, outfile):
for i in self.interpreter.get_targets().items():
for i in self.build.get_targets().items():
target = i[1]
self.generate_target(target, outfile)