Updating autogen script
This commit is contained in:
1 parent
feaac16804
commit
a1e0a4edbb
11 files changed
+161
-141
No files matched your search
+112
-92
@@ -3,6 +3,7 @@
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -39,7 +40,7 @@ INCLUDE_DIR = f"{OUTPUT_DIR}/{DIR_MAP['HEADER']}"
|
||||
# const
|
||||
SRC_DIR = f"{OUTPUT_DIR}/{DIR_MAP['SOURCE']}"
|
||||
# Where object files .o go
|
||||
OBJ_DIR = f'{BUILD_DIR}/obj'
|
||||
OBJ_DIR = BUILD_DIR
|
||||
# The name of the main test executable
|
||||
TEST_EXE = f'{OUTPUT_DIR}/main.exe'
|
||||
|
||||
@@ -53,10 +54,10 @@ CFLAGS = ['-Wall', '-Werror']
|
||||
CVFLAGS = ['--coverage', '-O0']
|
||||
# Include to the main Library
|
||||
# const
|
||||
INCLUDE = '-I ./src'
|
||||
INCLUDE = ['-I', './src']
|
||||
# Include to the test file headers
|
||||
# const
|
||||
TINCLUDE = f'-I {INCLUDE_DIR}'
|
||||
TINCLUDE = ['-I', f'{INCLUDE_DIR}']
|
||||
|
||||
# Macro expansion
|
||||
|
||||
@@ -115,25 +116,36 @@ COLLECTIONS = [
|
||||
]
|
||||
|
||||
|
||||
def full_path(path: Text) -> Text:
|
||||
'''
|
||||
Returns the full path of a 'path' with ralative paths, e.g. './' or '../'
|
||||
'''
|
||||
return str(pathlib.Path(path).resolve())
|
||||
|
||||
|
||||
def execute_command(commands: List[Text]):
|
||||
print(' '.join(commands))
|
||||
subprocess.run(commands, shell=True)
|
||||
cp = subprocess.run(commands, shell=True)
|
||||
|
||||
# if cp.returncode != 0:
|
||||
# print('Error when executing the last command')
|
||||
# exit(1)
|
||||
|
||||
|
||||
def assert_executable(program: Text):
|
||||
'''
|
||||
Asserts that a given program is installed and available in PATH
|
||||
Asserts that a given program is installed and available in PATH.
|
||||
'''
|
||||
if shutil.which(program) is None:
|
||||
raise Exception(f'The required executable {program} was not found in your path')
|
||||
|
||||
|
||||
def assert_dir(dir: Text):
|
||||
def assert_dir(directory: Text):
|
||||
'''
|
||||
Given a string 'dir' that is a path to a directory, make sure that it exists.
|
||||
Given a string 'directory' that is a path to a directory, make sure that it exists.
|
||||
'''
|
||||
if not os.path.exists(dir):
|
||||
os.makedirs(dir)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
|
||||
def assert_structure():
|
||||
@@ -143,13 +155,13 @@ def assert_structure():
|
||||
assert_dir(OUTPUT_DIR)
|
||||
|
||||
# include and src
|
||||
for dir in DIR_MAP.values():
|
||||
assert_dir(f'{OUTPUT_DIR}/{dir}')
|
||||
for directory in DIR_MAP.values():
|
||||
assert_dir(f'{OUTPUT_DIR}/{directory}')
|
||||
|
||||
# For each library, create a directory for include and src
|
||||
for lib in ALL_LIBS:
|
||||
for dir in DIR_MAP.values():
|
||||
assert_dir(f'{OUTPUT_DIR}/{dir}/{lib.lower()}')
|
||||
for directory in DIR_MAP.values():
|
||||
assert_dir(f'{OUTPUT_DIR}/{directory}/{lib.lower()}')
|
||||
|
||||
assert_dir(BUILD_DIR)
|
||||
assert_dir(OBJ_DIR)
|
||||
@@ -158,12 +170,21 @@ def assert_structure():
|
||||
assert_dir(f'{OBJ_DIR}/{lib.lower()}')
|
||||
|
||||
|
||||
def clean_build():
|
||||
execute_command(['find', BUILD_DIR, '-type', 'f', '-delete'])
|
||||
execute_command(['find', '.', '-iname', '*.gcov', '-type', 'f', '-delete'])
|
||||
def clean_build(cleanfull: bool):
|
||||
'''
|
||||
Removes all files generated by compilation and code coverage along with folders.
|
||||
'''
|
||||
execute_command(['rm', '-r', '*.gcov'])
|
||||
execute_command(['rm', TEST_EXE])
|
||||
|
||||
for directory in [BUILD_DIR, SRC_DIR, INCLUDE_DIR]:
|
||||
execute_command(['rm', '-r', directory])
|
||||
|
||||
|
||||
def expand_code():
|
||||
'''
|
||||
Expands code to be used in tests. This is necessary because code coverage doesn't work with macros.
|
||||
'''
|
||||
# Generate Header and Source separately
|
||||
for ftype in ['HEADER', 'SOURCE']:
|
||||
for data in COLLECTIONS:
|
||||
@@ -183,7 +204,7 @@ def expand_code():
|
||||
file.flush()
|
||||
file.close()
|
||||
|
||||
result = subprocess.getoutput(f'{CC} {EXPAND_FLAGS} {INCLUDE} {TMP_FILE}')
|
||||
result = subprocess.getoutput(f'{CC} {EXPAND_FLAGS} {" ".join(INCLUDE)} {TMP_FILE}')
|
||||
|
||||
# (?s) makes '.' match anything, even '\n'
|
||||
match = re.search(fr'(?s){UNIQUE_FLAG}(?P<code>.+){UNIQUE_FLAG}', result)
|
||||
@@ -223,6 +244,9 @@ def expand_code():
|
||||
|
||||
|
||||
def format_expand():
|
||||
'''
|
||||
Format the files outputted by 'expand_code'.
|
||||
'''
|
||||
cmd = ['clang-format', '--style=file', '--verbose', '-i']
|
||||
dirs_to_format = [f'{OUTPUT_DIR}/{DIR_MAP[ftype]}/{lib.lower()}/*.{EXT_MAP[ftype]}' for ftype in DIR_MAP.keys() for lib in ALL_LIBS]
|
||||
cmd.extend(dirs_to_format)
|
||||
@@ -230,79 +254,63 @@ def format_expand():
|
||||
|
||||
|
||||
def format_code(source: bool, tests: bool):
|
||||
'''
|
||||
Format all source files from the main library or test files.
|
||||
'''
|
||||
if source:
|
||||
execute_command(['find', './src/', '-iname', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i'])
|
||||
execute_command(['find', './src/', '-name', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i'])
|
||||
if tests:
|
||||
execute_command(['find', './tests/', '-iname', '*.c', '-o', '-iname', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i'])
|
||||
execute_command(['find', './tests/', '-name', '*.c', '-o', '-name', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i'])
|
||||
|
||||
|
||||
def build_tests(with_coverage: bool):
|
||||
'''
|
||||
Build all object files and the main executable for tests
|
||||
'''
|
||||
cflags = CFLAGS.copy()
|
||||
|
||||
if with_coverage:
|
||||
cflags += CVFLAGS
|
||||
|
||||
print("WIP")
|
||||
|
||||
|
||||
def run_tests():
|
||||
execute_command([TEST_EXE])
|
||||
|
||||
|
||||
def unused():
|
||||
# Makefile
|
||||
# Contains many usefull things dealing with tests, code coverage and formatting
|
||||
|
||||
print('Updating Makefile...')
|
||||
|
||||
file = open(f'Makefile', 'w')
|
||||
|
||||
file.write(f'''# This file was generated by a Python script
|
||||
CC = {CC}
|
||||
CFLAGS = -std=c11 -Wall -Wextra
|
||||
CFLAGS += -Wno-missing-braces -DCMC_TEST_COLOR
|
||||
CVFLAGS = --coverage -O0 -g3
|
||||
INCLUDE = {INCLUDE_DIR} -I src
|
||||
|
||||
# Runs the script to update tests, run all tests and code coverage
|
||||
all: script coverage
|
||||
|
||||
tests: script build
|
||||
\t{OUTPUT_DIR}/main.exe
|
||||
|
||||
# Like build but with coverage
|
||||
coverage: {' '.join([f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS])} {OBJ_DIR}/main.o
|
||||
\t$(CC) $^ -o {OUTPUT_DIR}/main.exe $(CFLAGS) $(CVFLAGS)
|
||||
\t{OUTPUT_DIR}/main.exe
|
||||
\tgcov {OBJ_DIR}/main.o {' '.join([f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS])}
|
||||
|
||||
# Run without coverage
|
||||
run: build
|
||||
\t{OUTPUT_DIR}/main.exe
|
||||
|
||||
script:
|
||||
\tpython autogen.py
|
||||
|
||||
clean:
|
||||
\tfind {BUILD_DIR} -type f -delete
|
||||
\trm *.gcov
|
||||
|
||||
# No coverage build
|
||||
build: {' '.join([f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS])} {OBJ_DIR}/main.o
|
||||
\t$(CC) $^ -o {OUTPUT_DIR}/main.exe $(CFLAGS)
|
||||
|
||||
{OBJ_DIR}/main.o: {OUTPUT_DIR}/main.c
|
||||
\t$(CC) -c $^ -o $@ -I $(INCLUDE) $(CFLAGS)
|
||||
''')
|
||||
else:
|
||||
cflags += ['-O3']
|
||||
|
||||
for data in COLLECTIONS:
|
||||
file.write(f'''
|
||||
{OBJ_DIR}/{data['LIB'].lower()}/{data['SNAME']}.o: {SRC_DIR}/{data['LIB'].lower()}/{data['SNAME']}.c {INCLUDE_DIR}/{data['LIB'].lower()}/{data['SNAME']}.h
|
||||
\t$(CC) -c $< -o $@ -I $(INCLUDE) $(CFLAGS) $(CVFLAGS)
|
||||
''')
|
||||
cmd = [CC]
|
||||
cmd += cflags
|
||||
cmd += TINCLUDE # This one needs to go first
|
||||
cmd += INCLUDE
|
||||
cmd += ['-c', f"{SRC_DIR}/{data['LIB'].lower()}/{data['SNAME']}.c"]
|
||||
cmd += ['-o', f"{OBJ_DIR}/{data['LIB'].lower()}/{data['SNAME']}.o"]
|
||||
execute_command(cmd)
|
||||
|
||||
file.close()
|
||||
print('\nDone')
|
||||
# Build OUTPUT_DIR/main.c
|
||||
cmd = [CC]
|
||||
cmd += cflags
|
||||
cmd += TINCLUDE # This one needs to go first
|
||||
cmd += INCLUDE
|
||||
cmd += ['-c', f'{OUTPUT_DIR}/main.c']
|
||||
cmd += ['-o', f"{OBJ_DIR}/main.o"]
|
||||
execute_command(cmd)
|
||||
|
||||
# Link everything producing OUTPUT_DIR/main.exe
|
||||
cmd = [CC]
|
||||
cmd += cflags
|
||||
cmd += ['-o', TEST_EXE]
|
||||
cmd += [f'{OBJ_DIR}/main.o']
|
||||
cmd += [f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS]
|
||||
execute_command(cmd)
|
||||
|
||||
|
||||
def run_tests(coverage: bool):
|
||||
'''
|
||||
Runs tests. If coverage, run gcov.
|
||||
'''
|
||||
execute_command([full_path(TEST_EXE)])
|
||||
|
||||
if coverage:
|
||||
objects = [f'{OBJ_DIR}/main.o']
|
||||
objects += [f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS]
|
||||
execute_command(['gcov'] + objects)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -320,6 +328,11 @@ if __name__ == '__main__':
|
||||
default=False,
|
||||
action='store_true',
|
||||
dest='clean')
|
||||
tests.add_argument('-cf', '--clean-full',
|
||||
help='Cleans all build objects, source and header files, and removes folders',
|
||||
default=False,
|
||||
action='store_true',
|
||||
dest='cleanfull')
|
||||
tests.add_argument('-e', '--expand',
|
||||
help='Expands the header and source files',
|
||||
default=False,
|
||||
@@ -330,18 +343,23 @@ if __name__ == '__main__':
|
||||
default=False,
|
||||
action='store_true',
|
||||
dest='noformat')
|
||||
tests.add_argument('-b', '--build',
|
||||
help='Compile the object files and the main executable',
|
||||
default=False,
|
||||
action='store_true',
|
||||
dest='build')
|
||||
tests.add_argument('-r', '--run',
|
||||
help='Runs tests without code coverage',
|
||||
help='Runs tests, assuming that it is already built',
|
||||
default=False,
|
||||
action='store_true',
|
||||
dest='run')
|
||||
tests.add_argument('-cv', '--coverage',
|
||||
help='Compiles and runs tests with code coverage',
|
||||
help='Adds the code coverage flags for compilation',
|
||||
default=False,
|
||||
action='store_true',
|
||||
dest='coverage')
|
||||
tests.add_argument('-rr', '--rerun',
|
||||
help='Builds tests without code coverage and runs it',
|
||||
help='Compile the object files again and then run the tests',
|
||||
default=False,
|
||||
action='store_true',
|
||||
dest='rerun')
|
||||
@@ -365,11 +383,12 @@ if __name__ == '__main__':
|
||||
print(CONFIG)
|
||||
exit(0)
|
||||
|
||||
if args.clean:
|
||||
clean_build()
|
||||
if args.clean or args.cleanfull:
|
||||
clean_build(args.cleanfull)
|
||||
|
||||
assert_structure()
|
||||
assert_executable(CC)
|
||||
if not args.cleanfull:
|
||||
assert_structure()
|
||||
assert_executable(CC)
|
||||
|
||||
if args.expand:
|
||||
expand_code()
|
||||
@@ -382,11 +401,12 @@ if __name__ == '__main__':
|
||||
assert_executable('xargs')
|
||||
format_code(args.fmt_source, args.fmt_tests)
|
||||
|
||||
if args.rerun:
|
||||
build_tests(False)
|
||||
run_tests()
|
||||
if args.build:
|
||||
build_tests(args.coverage)
|
||||
if args.run:
|
||||
run_tests(args.coverage)
|
||||
elif args.rerun:
|
||||
build_tests(args.coverage)
|
||||
run_tests(args.coverage)
|
||||
elif args.run:
|
||||
run_tests()
|
||||
elif args.coverage:
|
||||
build_tests(True)
|
||||
run_tests()
|
||||
run_tests(args.coverage)
|
||||
Reference in new issue
Block a user