|
15 | 15 | import nunavut |
16 | 16 | import nunavut.lang |
17 | 17 | import nunavut.jinja |
18 | | - |
| 18 | +from ._lockfile import Lockfile |
19 | 19 |
|
20 | 20 | _AnyPath = Union[str, pathlib.Path] |
21 | 21 |
|
@@ -149,101 +149,83 @@ def compile( # pylint: disable=redefined-builtin |
149 | 149 | composite_types: list[pydsdl.CompositeType] = [] |
150 | 150 | output_directory = pathlib.Path(pathlib.Path.cwd() if output_directory is None else output_directory).resolve() |
151 | 151 |
|
152 | | - if root_namespace_directory is not None: |
153 | | - root_namespace_directory = pathlib.Path(root_namespace_directory).resolve() |
154 | | - if root_namespace_directory.parent == output_directory: |
155 | | - # https://github.com/OpenCyphal/pycyphal/issues/133 and https://github.com/OpenCyphal/pycyphal/issues/127 |
156 | | - raise ValueError( |
157 | | - "The specified destination may overwrite the DSDL root namespace directory. " |
158 | | - "Consider specifying a different output directory instead." |
| 152 | + lockfile = Lockfile(root_namespace_directory, root_namespace_name, output_directory) |
| 153 | + lockfile_created = lockfile.create() |
| 154 | + |
| 155 | + if lockfile_created: |
| 156 | + if root_namespace_directory is not None: |
| 157 | + root_namespace_directory = pathlib.Path(root_namespace_directory).resolve() |
| 158 | + if root_namespace_directory.parent == output_directory: |
| 159 | + # https://github.com/OpenCyphal/pycyphal/issues/133 and https://github.com/OpenCyphal/pycyphal/issues/127 |
| 160 | + raise ValueError( |
| 161 | + "The specified destination may overwrite the DSDL root namespace directory. " |
| 162 | + "Consider specifying a different output directory instead." |
| 163 | + ) |
| 164 | + |
| 165 | + # Read the DSDL definitions |
| 166 | + composite_types = pydsdl.read_namespace( |
| 167 | + root_namespace_directory=str(root_namespace_directory), |
| 168 | + lookup_directories=list(map(str, lookup_directories or [])), |
| 169 | + allow_unregulated_fixed_port_id=allow_unregulated_fixed_port_id, |
| 170 | + ) |
| 171 | + if not composite_types: |
| 172 | + _logger.info("Root namespace directory %r does not contain DSDL definitions", root_namespace_directory) |
| 173 | + return None |
| 174 | + (root_namespace_name,) = set(map(lambda x: x.root_namespace, composite_types)) # type: ignore |
| 175 | + _logger.info("Read %d definitions from root namespace %r", len(composite_types), root_namespace_name) |
| 176 | + |
| 177 | + # Generate code |
| 178 | + assert isinstance(output_directory, pathlib.Path) |
| 179 | + root_ns = nunavut.build_namespace_tree( |
| 180 | + types=composite_types, |
| 181 | + root_namespace_dir=str(root_namespace_directory), |
| 182 | + output_dir=str(output_directory), |
| 183 | + language_context=language_context, |
| 184 | + ) |
| 185 | + code_generator = nunavut.jinja.DSDLCodeGenerator( |
| 186 | + namespace=root_ns, |
| 187 | + generate_namespace_types=nunavut.YesNoDefault.YES, |
| 188 | + followlinks=True, |
| 189 | + ) |
| 190 | + code_generator.generate_all() |
| 191 | + _logger.info( |
| 192 | + "Generated %d types from the root namespace %r in %.1f seconds", |
| 193 | + len(composite_types), |
| 194 | + root_namespace_name, |
| 195 | + time.monotonic() - started_at, |
| 196 | + ) |
| 197 | + else: |
| 198 | + root_ns = nunavut.build_namespace_tree( |
| 199 | + types=[], |
| 200 | + root_namespace_dir=str(""), |
| 201 | + output_dir=str(output_directory), |
| 202 | + language_context=language_context, |
159 | 203 | ) |
160 | 204 |
|
161 | | - # Read the DSDL definitions |
162 | | - composite_types = pydsdl.read_namespace( |
163 | | - root_namespace_directory=str(root_namespace_directory), |
164 | | - lookup_directories=list(map(str, lookup_directories or [])), |
165 | | - allow_unregulated_fixed_port_id=allow_unregulated_fixed_port_id, |
166 | | - ) |
167 | | - if not composite_types: |
168 | | - _logger.info("Root namespace directory %r does not contain DSDL definitions", root_namespace_directory) |
169 | | - return None |
170 | | - (root_namespace_name,) = set(map(lambda x: x.root_namespace, composite_types)) # type: ignore |
171 | | - _logger.info("Read %d definitions from root namespace %r", len(composite_types), root_namespace_name) |
172 | | - |
173 | | - # Generate code |
174 | | - assert isinstance(output_directory, pathlib.Path) |
175 | | - root_ns = nunavut.build_namespace_tree( |
176 | | - types=composite_types, |
177 | | - root_namespace_dir=str(root_namespace_directory), |
178 | | - output_dir=str(output_directory), |
179 | | - language_context=language_context, |
180 | | - ) |
181 | | - code_generator = nunavut.jinja.DSDLCodeGenerator( |
| 205 | + support_generator = nunavut.jinja.SupportGenerator( |
182 | 206 | namespace=root_ns, |
183 | | - generate_namespace_types=nunavut.YesNoDefault.YES, |
184 | | - followlinks=True, |
185 | | - ) |
186 | | - code_generator.generate_all() |
187 | | - _logger.info( |
188 | | - "Generated %d types from the root namespace %r in %.1f seconds", |
189 | | - len(composite_types), |
190 | | - root_namespace_name, |
191 | | - time.monotonic() - started_at, |
192 | | - ) |
193 | | - else: |
194 | | - root_ns = nunavut.build_namespace_tree( |
195 | | - types=[], |
196 | | - root_namespace_dir=str(""), |
197 | | - output_dir=str(output_directory), |
198 | | - language_context=language_context, |
199 | 207 | ) |
| 208 | + support_generator.generate_all() |
200 | 209 |
|
201 | | - lockfile = f"{root_namespace_name}.lock" |
202 | | - if not root_namespace_name: |
203 | | - lockfile = "support.lock" |
204 | | - lockfile_path = f"{output_directory}/{lockfile}" |
205 | | - |
206 | | - while True: |
207 | | - if not os.path.exists(lockfile_path): |
208 | | - try: |
209 | | - pathlib.Path(output_directory).mkdir(parents=True, exist_ok=True) |
210 | | - pathlib.Path(lockfile_path).touch() |
| 210 | + # A minor UX improvement; see https://github.com/OpenCyphal/pycyphal/issues/115 |
| 211 | + for p in sys.path: |
| 212 | + if pathlib.Path(p).resolve() == pathlib.Path(output_directory): |
211 | 213 | break |
212 | | - except PermissionError: |
213 | | - _logger.warning("Unable to create lockfile at %s", lockfile_path) |
214 | | - else: |
215 | | - time.sleep(1) |
216 | | - if pathlib.Path.exists(output_directory / pathlib.Path(root_namespace_name)): |
217 | | - return GeneratedPackageInfo( |
218 | | - path=pathlib.Path(output_directory) / pathlib.Path(root_namespace_name), |
219 | | - models=composite_types, |
220 | | - name=root_namespace_name, |
221 | | - ) |
222 | | - |
223 | | - support_generator = nunavut.jinja.SupportGenerator( |
224 | | - namespace=root_ns, |
225 | | - ) |
226 | | - support_generator.generate_all() |
227 | | - |
228 | | - # A minor UX improvement; see https://github.com/OpenCyphal/pycyphal/issues/115 |
229 | | - for p in sys.path: |
230 | | - if pathlib.Path(p).resolve() == pathlib.Path(output_directory): |
231 | | - break |
232 | | - else: |
233 | | - if os.name == "nt": |
234 | | - quick_fix = f'Quick fix: `$env:PYTHONPATH += ";{output_directory.resolve()}"`' |
235 | | - elif os.name == "posix": |
236 | | - quick_fix = f'Quick fix: `export PYTHONPATH="{output_directory.resolve()}"`' |
237 | 214 | else: |
238 | | - quick_fix = "Quick fix is not available for this OS." |
239 | | - _logger.info( |
240 | | - "Generated package is stored in %r, which is not in Python module search path list. " |
241 | | - "The package will fail to import unless you add the destination directory to sys.path or PYTHONPATH. %s", |
242 | | - str(output_directory), |
243 | | - quick_fix, |
244 | | - ) |
| 215 | + if os.name == "nt": |
| 216 | + quick_fix = f'Quick fix: `$env:PYTHONPATH += ";{output_directory.resolve()}"`' |
| 217 | + elif os.name == "posix": |
| 218 | + quick_fix = f'Quick fix: `export PYTHONPATH="{output_directory.resolve()}"`' |
| 219 | + else: |
| 220 | + quick_fix = "Quick fix is not available for this OS." |
| 221 | + _logger.info( |
| 222 | + "Generated package is stored in %r, which is not in Python module search path list. " |
| 223 | + "The package will fail to import unless you add the destination directory to sys.path or PYTHONPATH. %s", |
| 224 | + str(output_directory), |
| 225 | + quick_fix, |
| 226 | + ) |
245 | 227 |
|
246 | | - pathlib.Path(lockfile_path).unlink() |
| 228 | + lockfile.remove() |
247 | 229 |
|
248 | 230 | return GeneratedPackageInfo( |
249 | 231 | path=pathlib.Path(output_directory) / pathlib.Path(root_namespace_name), |
|
0 commit comments