diff --git a/Makefile b/Makefile index 0a44c3b16..24720037c 100644 --- a/Makefile +++ b/Makefile @@ -72,4 +72,7 @@ cross-compile: FORCE: ; dependencies: FORCE - go run bypy/devenv.go + go run bypy/devenv.go deps + +develop: FORCE + go run bypy/devenv.go build diff --git a/bypy/devenv.go b/bypy/devenv.go index f0803c9bc..aa1fa4bc3 100755 --- a/bypy/devenv.go +++ b/bypy/devenv.go @@ -4,6 +4,7 @@ package main import ( "bytes" + "errors" "fmt" "io" "io/fs" @@ -26,6 +27,10 @@ func exit(x any) { if v == nil { os.Exit(0) } + var ee *exec.ExitError + if errors.As(v, &ee) { + os.Exit(ee.ExitCode()) + } case string: if v == "" { os.Exit(0) @@ -33,10 +38,11 @@ func exit(x any) { case int: os.Exit(v) } - fmt.Fprintf(os.Stderr, "\x1b[31mError\x1b[m: %s", x) + fmt.Fprintf(os.Stderr, "\x1b[31mError\x1b[m: %s\n", x) os.Exit(1) } +// download deps {{{ func cached_download(url string) string { fname := filepath.Base(url) fmt.Println("Downloading", fname) @@ -87,12 +93,15 @@ func relocate_pkgconfig(path, old_prefix, new_prefix string) error { return os.WriteFile(path, nraw, 0o644) } -func main() { +func chdir_to_base() { _, filename, _, _ := runtime.Caller(0) base_dir := filepath.Dir(filepath.Dir(filename)) if err := os.Chdir(base_dir); err != nil { exit(err) } +} +func dependencies(args []string) { + chdir_to_base() data, err := os.ReadFile(".github/workflows/ci.py") if err != nil { exit(err) @@ -142,11 +151,65 @@ func main() { if err != nil { return err } - if d.Type().IsRegular() && strings.HasSuffix(d.Name(), ".pc") { - err = relocate_pkgconfig(path, prefix, root) + if d.Type().IsRegular() { + name := d.Name() + ext := filepath.Ext(name) + if ext == ".pc" || (ext == ".py" && strings.HasPrefix(name, "_sysconfigdata_")) { + err = relocate_pkgconfig(path, prefix, root) + } } return err }); err != nil { exit(err) } + fmt.Println(`Dependencies downloaded. Now build kitty with: make develop`) +} + +// }}} + +func prepend(env_var, path string) { + val := os.Getenv(env_var) + if val != "" { + val = string(filepath.ListSeparator) + val + } + os.Setenv(env_var, path+val) +} + +func build(args []string) { + chdir_to_base() + python := "" + root, _ := filepath.Abs(filepath.Join(folder, "root")) + path_args := []string{"-I" + filepath.Join(root, "include"), "-L" + filepath.Join(root, "lib")} + os.Setenv("DEVELOP_ROOT", root) + prepend("PKG_CONFIG_PATH", filepath.Join(root, "lib", "pkgconfig")) + switch runtime.GOOS { + case "linux": + prepend("LD_LIBRARY_PATH", filepath.Join(root, "lib")) + os.Setenv("PYTHONHOME", root) + python = filepath.Join(root, "bin", "python") + default: + exit("Building is only supported on Linux and macOS") + } + args = append([]string{"setup.py", "develop", "--debug"}, path_args...) + cmd := exec.Command(python, args...) + cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintln(os.Stderr, "The following build command failed:", python, strings.Join(args, " ")) + exit(err) + } + fmt.Println("Build successful. Run kitty as: kitty/launcher/kitty") +} + +func main() { + if len(os.Args) < 2 { + exit(`Expected "deps" or "build" subcommands`) + } + switch os.Args[1] { + case "deps": + dependencies(os.Args[2:]) + case "build": + build(os.Args[2:]) + default: + exit(`Expected "deps" or "build" subcommands`) + } } diff --git a/kitty/launcher/main.c b/kitty/launcher/main.c index 2c067d1de..f923760e6 100644 --- a/kitty/launcher/main.c +++ b/kitty/launcher/main.c @@ -194,7 +194,12 @@ run_embedded(RunData *run_data) { if (PyStatus_Exception(status)) goto fail; status = PyConfig_SetBytesString(&config, &config.run_filename, run_data->lib_dir); if (PyStatus_Exception(status)) goto fail; - +#ifdef SET_PYTHON_HOME + char pyhome[256]; + snprintf(pyhome, sizeof(pyhome), "%s/%s", run_data->lib_dir, SET_PYTHON_HOME); + status = PyConfig_SetBytesString(&config, &config.home, pyhome); + if (PyStatus_Exception(status)) goto fail; +#endif status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)) goto fail; PyConfig_Clear(&config); diff --git a/setup.py b/setup.py index d1d16a3a1..efb85e52e 100755 --- a/setup.py +++ b/setup.py @@ -1063,11 +1063,19 @@ def build_launcher(args: Options, launcher_dir: str = '.', bundle_type: str = 's cppflags.append(f'-DKITTY_LIB_DIR_NAME="{args.libdir_name}"') elif bundle_type == 'source': cppflags.append('-DFROM_SOURCE') + elif bundle_type == 'develop': + cppflags.append('-DFROM_SOURCE') + ph = os.path.relpath(os.environ["DEVELOP_ROOT"], '.') + cppflags.append(f'-DSET_PYTHON_HOME="{ph}"') + if is_macos: + pass + else: + ldflags += ['-Wl,--disable-new-dtags', f'-Wl,-rpath,$ORIGIN/../../{ph}/lib'] if bundle_type.startswith('macos-'): klp = '../Resources/kitty' elif bundle_type.startswith('linux-'): klp = '../{}/kitty'.format(args.libdir_name.strip('/')) - elif bundle_type == 'source': + elif bundle_type in ('source', 'develop'): klp = os.path.relpath('.', launcher_dir) else: raise SystemExit(f'Unknown bundle type: {bundle_type}') @@ -1569,7 +1577,7 @@ def safe_remove(*entries: str) -> None: def excluded(root: str, d: str) -> bool: q = os.path.relpath(os.path.join(root, d), src_base).replace(os.sep, '/') - return q in ('.git', 'bypy/b') + return q in ('.git', 'bypy/b', 'dependencies') for root, dirs, files in os.walk(src_base, topdown=True): dirs[:] = [d for d in dirs if not excluded(root, d)] @@ -1599,6 +1607,7 @@ def option_parser() -> argparse.ArgumentParser: # {{{ default=Options.action, choices=('build', 'test', + 'develop', 'linux-package', 'kitty.app', 'linux-freeze', @@ -1838,6 +1847,10 @@ def main() -> None: else: build_launcher(args, launcher_dir=launcher_dir) build_static_kittens(args, launcher_dir=launcher_dir) + elif args.action == 'develop': + build(args) + build_launcher(args, launcher_dir=launcher_dir, bundle_type='develop') + build_static_kittens(args, launcher_dir=launcher_dir) elif args.action == 'build-launcher': init_env_from_args(args, False) build_launcher(args, launcher_dir=launcher_dir)