diff --git a/Makefile b/Makefile index 5231798c5..dfc968b86 100644 --- a/Makefile +++ b/Makefile @@ -61,3 +61,13 @@ docs: man html develop-docs: $(MAKE) -C docs develop-docs + + +prepare-for-cross-compile: + $(MAKE) clean + $(MAKE) build + python3 setup.py $(VVAL) clean --clean-for-cross-compile + +cross-compile: + python3 setup.py linux-package --skip-code-generation + diff --git a/docs/build.rst b/docs/build.rst index bd8a73c77..5082892d7 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -204,3 +204,19 @@ the kitty program expects to find them there. This applies to creating packages for |kitty| for macOS package managers such as Homebrew or MacPorts as well. + +Cross-compilation +------------------- + +While cross compilation is neither officially supported, nor recommended, as it +means the test suite cannot be run for the cross-compiled build, there is some +support for cross-compilation. Basically, run:: + + make prepare-for-cross-compile + +Then setup the cross compile environment (CC, CFLAGS, etc.) and run:: + + make cross-compile + +This will create the cross compiled build in the :file:`linux-package` +directory. diff --git a/setup.py b/setup.py index 1d3c76f0f..2b1ba6fde 100755 --- a/setup.py +++ b/setup.py @@ -62,6 +62,8 @@ class Options(argparse.Namespace): sanitize: bool = False prefix: str = './linux-package' dir_for_static_binaries: str = 'build/static' + skip_code_generation: bool = False + clean_for_cross_compile: bool = False incremental: bool = True profile: bool = False libdir_name: str = 'lib' @@ -841,16 +843,17 @@ def extract_rst_targets() -> Dict[str, Dict[str, str]]: return cast(Dict[str, Dict[str, str]], m['main']()) -def build_ref_map() -> str: - d = extract_rst_targets() - h = 'static const char docs_ref_map[] = {\n' + textwrap.fill(', '.join(map(str, bytearray(json.dumps(d).encode('utf-8'))))) + '\n};\n' +def build_ref_map(skip_generation: bool = False) -> str: dest = 'kitty/docs_ref_map_generated.h' - q = '' - with suppress(FileNotFoundError), open(dest) as f: - q = f.read() - if q != h: - with open(dest, 'w') as f: - f.write(h) + if not skip_generation: + d = extract_rst_targets() + h = 'static const char docs_ref_map[] = {\n' + textwrap.fill(', '.join(map(str, bytearray(json.dumps(d).encode('utf-8'))))) + '\n};\n' + q = '' + with suppress(FileNotFoundError), open(dest) as f: + q = f.read() + if q != h: + with open(dest, 'w') as f: + f.write(h) return dest @@ -868,7 +871,7 @@ def build(args: Options, native_optimizations: bool = True, call_init: bool = Tr if call_init: init_env_from_args(args, native_optimizations) sources, headers = find_c_files() - headers.append(build_ref_map()) + headers.append(build_ref_map(args.skip_code_generation)) compile_c_extension( kitty_env(), 'kitty/fast_data_types', args.compilation_database, sources, headers ) @@ -881,6 +884,9 @@ def safe_makedirs(path: str) -> None: def update_go_generated_files(args: Options, kitty_exe: str) -> None: + if args.skip_code_generation: + print('Skipping generation of Go files due to command line option', flush=True) + return # update all the various auto-generated go files, if needed if args.verbose: print('Updating Go generated files...', flush=True) @@ -1493,7 +1499,7 @@ def clean_launcher_dir(launcher_dir: str) -> None: os.remove(x) -def clean() -> None: +def clean(for_cross_compile: bool = False) -> None: def safe_remove(*entries: str) -> None: for x in entries: @@ -1506,7 +1512,9 @@ def clean() -> None: safe_remove( 'build', 'compile_commands.json', 'link_commands.json', 'linux-package', 'kitty.app', 'asan-launcher', - 'kitty-profile', 'docs/generated') + 'kitty-profile') + if not for_cross_compile: + safe_remove('docs/generated') clean_launcher_dir('kitty/launcher') def excluded(root: str, d: str) -> bool: @@ -1521,7 +1529,9 @@ def clean() -> None: dirs.remove(d) for f in files: ext = f.rpartition('.')[-1] - if ext in ('so', 'dylib', 'pyc', 'pyo') or f.endswith('_generated.h') or f.endswith('_generated.go') or f.endswith('_generated.bin'): + if ext in ('so', 'dylib', 'pyc', 'pyo') or (not for_cross_compile and ( + f.endswith('_generated.h') or f.endswith('_generated.go') or f.endswith('_generated.bin')) + ): os.unlink(os.path.join(root, f)) for x in glob.glob('glfw/wayland-*-protocol.[ch]'): os.unlink(x) @@ -1581,6 +1591,19 @@ def option_parser() -> argparse.ArgumentParser: # {{{ default=Options.dir_for_static_binaries, help='Where to create the static kitten binary' ) + p.add_argument( + '--skip-code-generation', + default=Options.skip_code_generation, + action='store_true', + help='Do not create the *_generated.* source files. This is useful if they' + ' have already been generated by a previous build, for example during a two-stage cross compilation.' + ) + p.add_argument( + '--clean-for-cross-compile', + default=Options.clean_for_cross_compile, + action='store_true', + help='Do not clean generated Go source files. Useful for cross-compilation.' + ) p.add_argument( '--full', dest='incremental', @@ -1743,7 +1766,7 @@ def main() -> None: texe = os.path.abspath(os.path.join(launcher_dir, 'kitty')) os.execl(texe, texe, '+launch', 'test.py') if args.action == 'clean': - clean() + clean(for_cross_compile=args.clean_for_cross_compile) return with CompilationDatabase(args.incremental) as cdb: