|
1 | 1 | #!/usr/bin/env python3
|
| 2 | +import io |
2 | 3 | import os
|
| 4 | +import sys |
3 | 5 | from glob import glob
|
| 6 | +from importlib import import_module |
4 | 7 | from importlib.util import module_from_spec, spec_from_file_location
|
5 | 8 |
|
6 | 9 | from setuptools import setup
|
@@ -50,34 +53,55 @@ def make_release_tree(self, base_dir, files):
|
50 | 53 | cmdclass = dict(sdist=_stgit_sdist, build_py=_stgit_build_py)
|
51 | 54 |
|
52 | 55 |
|
53 |
| -def _generate_code(): |
54 |
| - import stgit.commands |
55 |
| - from stgit.completion.bash import write_bash_completion |
56 |
| - from stgit.completion.fish import write_fish_completion |
57 |
| - |
| 56 | +# Carefully perform code generation. First check if generated code |
| 57 | +# exists. Only write-out generated code if the content is different. |
| 58 | +# This accommodates several setup.py contexts, including: |
| 59 | +# - When running from an sdist where code is already generated |
| 60 | +# - During pip installation where the stgit package is not in sys.path |
| 61 | +# - In a git worktree where the generated code may change frequently |
| 62 | +# - When running from a read-only directory/filesystem |
| 63 | +def _maybe_generate_code(): |
58 | 64 | base = os.path.abspath(os.path.dirname(__file__))
|
59 |
| - |
60 |
| - commands = stgit.commands.get_commands(allow_cached=False) |
61 |
| - with open(os.path.join(base, 'stgit', 'commands', 'cmdlist.py'), 'w') as f: |
62 |
| - stgit.commands.py_commands(commands, f) |
63 |
| - |
64 |
| - completion_dir = os.path.join(base, 'completion') |
65 |
| - os.makedirs(completion_dir, exist_ok=True) |
66 |
| - with open(os.path.join(completion_dir, 'stgit.bash'), 'w') as f: |
67 |
| - write_bash_completion(f) |
68 |
| - with open(os.path.join(completion_dir, 'stg.fish'), 'w') as f: |
69 |
| - write_fish_completion(f) |
70 |
| - |
71 |
| - |
72 |
| -# Attempt to generate completion scripts and cmdlist.py. When setup.py |
73 |
| -# is executed in the context of a pip installation, the stgit package |
74 |
| -# will not be available and thus this will fail with an Import error. |
75 |
| -# However, in that case the generated files will already be a part of |
76 |
| -# either the sdist or wheel package that pip is installing. |
77 |
| -try: |
78 |
| - _generate_code() |
79 |
| -except ImportError: |
80 |
| - print("Skipping stgit code generation") |
| 65 | + paths = dict( |
| 66 | + cmds=os.path.join(base, 'stgit', 'commands', 'cmdlist.py'), |
| 67 | + bash=os.path.join(base, 'completion', 'stgit.bash'), |
| 68 | + fish=os.path.join(base, 'completion', 'stg.fish'), |
| 69 | + ) |
| 70 | + |
| 71 | + existing_content = dict() |
| 72 | + for k, path in paths.items(): |
| 73 | + if os.path.exists(path): |
| 74 | + with open(path, 'r') as f: |
| 75 | + existing_content[k] = f.read() |
| 76 | + else: |
| 77 | + existing_content[k] = None |
| 78 | + |
| 79 | + sys.path.insert(0, base) |
| 80 | + try: |
| 81 | + try: |
| 82 | + gen_funcs = dict( |
| 83 | + cmds=import_module('stgit.commands').write_cmdlist_py, |
| 84 | + bash=import_module('stgit.completion.bash').write_bash_completion, |
| 85 | + fish=import_module('stgit.completion.fish').write_fish_completion, |
| 86 | + ) |
| 87 | + except ImportError: |
| 88 | + if all(existing_content.values()): |
| 89 | + return # Okay, all generated content exists |
| 90 | + else: |
| 91 | + raise RuntimeError('Cannot perform code generation') |
| 92 | + finally: |
| 93 | + sys.path.pop(0) |
| 94 | + |
| 95 | + for k, gen_func in gen_funcs.items(): |
| 96 | + with io.StringIO() as f: |
| 97 | + gen_func(f) |
| 98 | + new_content = f.getvalue() |
| 99 | + if existing_content[k] != new_content: |
| 100 | + with open(paths[k], 'w') as f: |
| 101 | + f.write(new_content) |
| 102 | + |
| 103 | + |
| 104 | +_maybe_generate_code() |
81 | 105 |
|
82 | 106 |
|
83 | 107 | setup(
|
|
0 commit comments