diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6efd5b4a..1fcef169 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,3 +42,16 @@ jobs: - name: Unit tests (IPv4) if: ${{ !cancelled() }} run: cd test && ./unit --ipv4 + + # Build the files that util/strip.py writes for upstreaming, which + # nothing else here compiles. --kernel keeps the code for this kernel + # in LINUX_VERSION_CODE blocks so that they build with it. -Werror + # makes stripping mistakes such as a dangling "else" fail. + - name: Build homa.ko from util/strip.py output + if: ${{ !cancelled() }} + run: | + out=$RUNNER_TEMP/net-next + mkdir -p $out/net $out/include/uapi/linux + make net-next LINUX_SRC_DIR=$out "STRIP_FLAGS=--kernel $LINUX_VERSION" + make -C $KDIR M=$out/net/homa CONFIG_HOMA=m KBUILD_MODPOST_WARN=1 \ + KCFLAGS="-I$out/include/uapi -Werror" modules diff --git a/Makefile b/Makefile index 8c8f17f9..e40a5796 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ net-next: $(HOMA_TARGET) $(CP_TARGETS) $(LINUX_SRC_DIR)/include/uapi/linux/homa. $(HOMA_TARGET): mkdir $(HOMA_TARGET) $(HOMA_TARGET)/%: % util/strip.py - util/strip.py $< > $@ + util/strip.py $(STRIP_FLAGS) $< > $@ $(HOMA_TARGET)/%.txt: %.txt cp $< $@ $(HOMA_TARGET)/Makefile: Makefile.upstream @@ -91,7 +91,7 @@ $(HOMA_TARGET)/Makefile: Makefile.upstream $(HOMA_TARGET)/strip_decl.py: util/strip_decl.py cp $< $@ $(LINUX_SRC_DIR)/include/uapi/linux/homa.h: homa.h util/strip.py - util/strip.py $< > $@ + util/strip.py $(STRIP_FLAGS) $< > $@ clean: $(MAKE) -C $(KDIR) M=$(shell pwd) clean diff --git a/util/strip.py b/util/strip.py index be80dbc7..9f472b97 100755 --- a/util/strip.py +++ b/util/strip.py @@ -7,12 +7,17 @@ a Linux kernel repo, removing information that doesn't belong in the official kernel version (such as calls to tt_record). -Usage: strip.py file file file ... destdir +Usage: strip.py [--kernel X.Y.Z] file file file ... destdir Each of the files will be read, stripped as appropriate, and copied to a file by the same name in destdir. If there is only a single file and no destdir, then the stripped file is printed on standard output. +Normally only the #else code of an '#if LINUX_VERSION_CODE' block is kept. +With --kernel, the code for that Linux version is kept instead, so that the +stripped files can be compiled against the kernel that the main branch is +built with (for testing only; this is not what goes upstream). + The following code is removed automatically: * Calls to timetracing, such as tt_record* * Blocks conditionalized on '#ifdef __UNIT_TEST__' @@ -141,6 +146,26 @@ def blank_next_ok(line: str) -> bool: return False return True +# Linux version for --kernel, as a (major, minor, patch) tuple, or None. +kernel = None + +def version_test(file: str, line_num: int, line: str) -> bool: + """ + Return the value of an '#if LINUX_VERSION_CODE' test for kernel. + """ + + global exit_code + + m = re.match(r'#if LINUX_VERSION_CODE\s*(<|>=)\s*' + r'KERNEL_VERSION\((\d+),\s*(\d+),\s*(\d+)\)', line) + if not m: + print('%s:%d: can\'t evaluate %s' % (file, line_num, line.strip()), + file=sys.stderr) + exit_code = 1 + return False + version = tuple(int(m.group(i)) for i in range(2, 5)) + return (kernel < version) == (m.group(1) == '<') + def scan(file: str) -> list[str]: """ Read a file, remove information that shouldn't appear in the Linux kernel @@ -179,6 +204,10 @@ def scan(file: str) -> list[str]: # '#if LINUX_VERSION_CODE' (this code should remain) in_version = None + # Which part of the current '#if LINUX_VERSION_CODE' block to keep: + # 'if' or 'else'. + keep_version = 'else' + # Array of lines containing the stripped version of the file slines = [] @@ -354,10 +383,14 @@ def scan(file: str) -> list[str]: if line.startswith('#else'): in_version = 'else' continue - if in_version == 'if': + if in_version != keep_version: continue elif line.startswith('#if LINUX_VERSION_CODE'): in_version = 'if' + if kernel and version_test(file, line_num, line): + keep_version = 'if' + else: + keep_version = 'else' if not blank_next_ok(slines[-1]): delete_empty_line = True continue @@ -410,15 +443,20 @@ def scan(file: str) -> list[str]: if __name__ == '__main__': f = sys.stdin - if len(sys.argv) < 2: - print('Usage: strip.py [--alt] file [file ... destdir]', file=sys.stderr) + args = sys.argv[1:] + if len(args) > 1 and args[0] == '--kernel': + kernel = tuple(int(x) for x in args[1].split('.')[:3]) + args = args[2:] + if len(args) < 1: + print('Usage: strip.py [--kernel X.Y.Z] file [file ... destdir]', + file=sys.stderr) exit(1) - if len(sys.argv) == 2: - for line in scan(sys.argv[1]): + if len(args) == 1: + for line in scan(args[0]): print(line, end='') else: - for file in sys.argv[1:-1]: - dst_file = '%s/%s' % (sys.argv[-1], file) + for file in args[:-1]: + dst_file = '%s/%s' % (args[-1], file) print('Stripping %s into %s' % (file, dst_file)) slines = scan(file) dst = open(dst_file, 'w')