laylight 0.0.2__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- laylight-0.2.2/.gitignore +33 -0
- laylight-0.2.2/PKG-INFO +68 -0
- {laylight-0.0.2 → laylight-0.2.2}/README.md +13 -12
- laylight-0.2.2/laylight/__init__.py +64 -0
- laylight-0.2.2/laylight/backend.py +143 -0
- laylight-0.2.2/laylight/font.py +157 -0
- laylight-0.2.2/laylight/geometry.py +704 -0
- laylight-0.2.2/laylight/ir.py +380 -0
- laylight-0.2.2/laylight/klayout_bridge.py +121 -0
- laylight-0.2.2/laylight/layout.py +1036 -0
- laylight-0.2.2/laylight/pcell.py +196 -0
- laylight-0.2.2/laylight/placable.py +588 -0
- laylight-0.2.2/laylight/primitives.py +6 -0
- {laylight-0.0.2 → laylight-0.2.2}/laylight/render.py +130 -155
- {laylight-0.0.2 → laylight-0.2.2}/laylight/render_all.py +194 -256
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/__init__.py +22 -20
- laylight-0.2.2/laylight/structure/abstract.py +266 -0
- laylight-0.2.2/laylight/structure/black_box.py +121 -0
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/connection.py +238 -265
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/coupler.py +469 -641
- laylight-0.2.2/laylight/structure/crossing.py +50 -0
- laylight-0.2.2/laylight/structure/curve.py +126 -0
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/cutback.py +99 -125
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/grating.py +522 -660
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/metal.py +187 -236
- laylight-0.2.2/laylight/structure/mmi.py +251 -0
- laylight-0.2.2/laylight/structure/mzi.py +139 -0
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/mzi_rect.py +332 -357
- laylight-0.2.2/laylight/structure/packaging.py +81 -0
- laylight-0.2.2/laylight/structure/prober.py +85 -0
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/racetrack.py +116 -173
- laylight-0.2.2/laylight/structure/ring.py +140 -0
- {laylight-0.0.2 → laylight-0.2.2}/laylight/structure/spiral.py +174 -200
- laylight-0.2.2/laylight/structure/taper.py +86 -0
- laylight-0.2.2/laylight/structure/transition.py +38 -0
- laylight-0.2.2/laylight/structure/wg.py +119 -0
- laylight-0.2.2/laylight/technology.py +62 -0
- laylight-0.2.2/pyproject.toml +34 -0
- laylight-0.0.2/.gitignore +0 -15
- laylight-0.0.2/PKG-INFO +0 -69
- laylight-0.0.2/laylight/__init__.py +0 -32
- laylight-0.0.2/laylight/candy.py +0 -42
- laylight-0.0.2/laylight/main.py +0 -6
- laylight-0.0.2/laylight/pcell.py +0 -183
- laylight-0.0.2/laylight/placable.py +0 -177
- laylight-0.0.2/laylight/primitives.py +0 -6
- laylight-0.0.2/laylight/structure/abstract.py +0 -144
- laylight-0.0.2/laylight/structure/black_box.py +0 -131
- laylight-0.0.2/laylight/structure/crossing.py +0 -84
- laylight-0.0.2/laylight/structure/curve.py +0 -246
- laylight-0.0.2/laylight/structure/mmi.py +0 -409
- laylight-0.0.2/laylight/structure/mzi.py +0 -244
- laylight-0.0.2/laylight/structure/packaging.py +0 -148
- laylight-0.0.2/laylight/structure/prober.py +0 -149
- laylight-0.0.2/laylight/structure/ring.py +0 -219
- laylight-0.0.2/laylight/structure/taper.py +0 -93
- laylight-0.0.2/laylight/structure/transition.py +0 -67
- laylight-0.0.2/laylight/structure/wg.py +0 -203
- laylight-0.0.2/laylight/technology.py +0 -21
- laylight-0.0.2/pyproject.toml +0 -21
- laylight-0.0.2/tests/.gitignore +0 -1
- laylight-0.0.2/tests/baseline/adiabatic_coupler_shape.png +0 -0
- laylight-0.0.2/tests/baseline/arc_curve_shape.png +0 -0
- laylight-0.0.2/tests/baseline/black_box_grating_shape.png +0 -0
- laylight-0.0.2/tests/baseline/black_box_mmi_shape.png +0 -0
- laylight-0.0.2/tests/baseline/black_box_shape.png +0 -0
- laylight-0.0.2/tests/baseline/contra_directional_coupler_shape.png +0 -0
- laylight-0.0.2/tests/baseline/crossing_shape.png +0 -0
- laylight-0.0.2/tests/baseline/cutback_shape.png +0 -0
- laylight-0.0.2/tests/baseline/die_with_pads_shape.png +0 -0
- laylight-0.0.2/tests/baseline/directional_coupler_shape.png +0 -0
- laylight-0.0.2/tests/baseline/fiber_array_shape.png +0 -0
- laylight-0.0.2/tests/baseline/grating_bragg_shape.png +0 -0
- laylight-0.0.2/tests/baseline/grating_elliptical_shape.png +0 -0
- laylight-0.0.2/tests/baseline/grating_focusing_shape.png +0 -0
- laylight-0.0.2/tests/baseline/grating_polarization_shape.png +0 -0
- laylight-0.0.2/tests/baseline/grating_rectangular_shape.png +0 -0
- laylight-0.0.2/tests/baseline/gsg_pad_shape.png +0 -0
- laylight-0.0.2/tests/baseline/heater_pad_shape.png +0 -0
- laylight-0.0.2/tests/baseline/heater_routing_shape.png +0 -0
- laylight-0.0.2/tests/baseline/metal_straight_shape.png +0 -0
- laylight-0.0.2/tests/baseline/metal_transition_shape.png +0 -0
- laylight-0.0.2/tests/baseline/mmi_shape.png +0 -0
- laylight-0.0.2/tests/baseline/mzi_rectangular_array_shape.png +0 -0
- laylight-0.0.2/tests/baseline/mzi_shape.png +0 -0
- laylight-0.0.2/tests/baseline/mzi_tensor_shape.png +0 -0
- laylight-0.0.2/tests/baseline/pad_shape.png +0 -0
- laylight-0.0.2/tests/baseline/pcell_library_shape.png +0 -0
- laylight-0.0.2/tests/baseline/racetrack_coupled_shape.png +0 -0
- laylight-0.0.2/tests/baseline/racetrack_shape.png +0 -0
- laylight-0.0.2/tests/baseline/ring_coupled_shape.png +0 -0
- laylight-0.0.2/tests/baseline/ring_shape.png +0 -0
- laylight-0.0.2/tests/baseline/scurve_shape.png +0 -0
- laylight-0.0.2/tests/baseline/spiral_shape.png +0 -0
- laylight-0.0.2/tests/baseline/strip_to_rib_transition_shape.png +0 -0
- laylight-0.0.2/tests/baseline/via_stack_shape.png +0 -0
- laylight-0.0.2/tests/baseline/wg_straight_shape.png +0 -0
- laylight-0.0.2/tests/baseline/ybranch_shape.png +0 -0
- laylight-0.0.2/tests/common.py +0 -115
- laylight-0.0.2/tests/output/adiabatic_coupler_shape.png +0 -0
- laylight-0.0.2/tests/output/arc_curve_shape.png +0 -0
- laylight-0.0.2/tests/output/black_box_grating_shape.png +0 -0
- laylight-0.0.2/tests/output/black_box_mmi_shape.png +0 -0
- laylight-0.0.2/tests/output/black_box_shape.png +0 -0
- laylight-0.0.2/tests/output/contra_directional_coupler_shape.png +0 -0
- laylight-0.0.2/tests/output/crossing_shape.png +0 -0
- laylight-0.0.2/tests/output/cutback_shape.png +0 -0
- laylight-0.0.2/tests/output/die_with_pads_shape.png +0 -0
- laylight-0.0.2/tests/output/directional_coupler_shape.png +0 -0
- laylight-0.0.2/tests/output/fiber_array_shape.png +0 -0
- laylight-0.0.2/tests/output/grating_bragg_shape.png +0 -0
- laylight-0.0.2/tests/output/grating_elliptical_shape.png +0 -0
- laylight-0.0.2/tests/output/grating_focusing_shape.png +0 -0
- laylight-0.0.2/tests/output/grating_polarization_shape.png +0 -0
- laylight-0.0.2/tests/output/grating_rectangular_shape.png +0 -0
- laylight-0.0.2/tests/output/gsg_pad_shape.png +0 -0
- laylight-0.0.2/tests/output/heater_pad_shape.png +0 -0
- laylight-0.0.2/tests/output/heater_routing_shape.png +0 -0
- laylight-0.0.2/tests/output/metal_straight_shape.png +0 -0
- laylight-0.0.2/tests/output/metal_transition_shape.png +0 -0
- laylight-0.0.2/tests/output/mmi_shape.png +0 -0
- laylight-0.0.2/tests/output/mzi_rectangular_array_shape.png +0 -0
- laylight-0.0.2/tests/output/mzi_shape.png +0 -0
- laylight-0.0.2/tests/output/mzi_tensor_shape.png +0 -0
- laylight-0.0.2/tests/output/pad_shape.png +0 -0
- laylight-0.0.2/tests/output/pcell_library_shape.png +0 -0
- laylight-0.0.2/tests/output/racetrack_coupled_shape.png +0 -0
- laylight-0.0.2/tests/output/racetrack_shape.png +0 -0
- laylight-0.0.2/tests/output/ring_coupled_shape.png +0 -0
- laylight-0.0.2/tests/output/ring_shape.png +0 -0
- laylight-0.0.2/tests/output/scurve_shape.png +0 -0
- laylight-0.0.2/tests/output/spiral_shape.png +0 -0
- laylight-0.0.2/tests/output/strip_to_rib_transition_shape.png +0 -0
- laylight-0.0.2/tests/output/via_stack_shape.png +0 -0
- laylight-0.0.2/tests/output/wg_straight_shape.png +0 -0
- laylight-0.0.2/tests/output/ybranch_shape.png +0 -0
- laylight-0.0.2/tests/test_black_box_shape.py +0 -154
- laylight-0.0.2/tests/test_coupler_shape.py +0 -79
- laylight-0.0.2/tests/test_crossing_shape.py +0 -29
- laylight-0.0.2/tests/test_curve_shape.py +0 -75
- laylight-0.0.2/tests/test_cutback_shape.py +0 -29
- laylight-0.0.2/tests/test_grating_shape.py +0 -94
- laylight-0.0.2/tests/test_metal_shape.py +0 -75
- laylight-0.0.2/tests/test_mmi_shape.py +0 -51
- laylight-0.0.2/tests/test_mzi_rect_shape.py +0 -128
- laylight-0.0.2/tests/test_mzi_shape.py +0 -137
- laylight-0.0.2/tests/test_packaging_shape.py +0 -45
- laylight-0.0.2/tests/test_pcell.py +0 -64
- laylight-0.0.2/tests/test_pcell_library.py +0 -178
- laylight-0.0.2/tests/test_prober_shape.py +0 -74
- laylight-0.0.2/tests/test_racetrack_shape.py +0 -29
- laylight-0.0.2/tests/test_render.py +0 -140
- laylight-0.0.2/tests/test_resonator_shape.py +0 -92
- laylight-0.0.2/tests/test_spiral_shape.py +0 -29
- laylight-0.0.2/tests/test_waveguide_shape.py +0 -76
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/target
|
|
2
|
+
node_modules/
|
|
3
|
+
dist/
|
|
4
|
+
packages/laylight-viewer-demo/public/
|
|
5
|
+
|
|
6
|
+
# large local test fixture (kept out of git; re-obtain or regenerate locally)
|
|
7
|
+
samples/gds/large-test.gds
|
|
8
|
+
samples/gds/large-test.gds.gz
|
|
9
|
+
|
|
10
|
+
# generated perf-bench fixture (regenerate: cargo run --release -p psdb --example gen_bench_gds)
|
|
11
|
+
samples/gds/bench-gen.gds
|
|
12
|
+
samples/gds/bench-gen.gds.gz
|
|
13
|
+
|
|
14
|
+
# generated CI perf-gate fixture (regenerated by scripts/perf-gate.mjs)
|
|
15
|
+
samples/gds/bench-gate.gds
|
|
16
|
+
|
|
17
|
+
# Python workspace (root pyproject.toml, packages under pypkg/)
|
|
18
|
+
__pycache__/
|
|
19
|
+
*.py[oc]
|
|
20
|
+
*.egg-info
|
|
21
|
+
.venv
|
|
22
|
+
.pytest_cache
|
|
23
|
+
.local
|
|
24
|
+
build/
|
|
25
|
+
wheels/
|
|
26
|
+
|
|
27
|
+
# laylight docs site build output (packages/laylight-docs)
|
|
28
|
+
packages/laylight-docs/.next/
|
|
29
|
+
packages/laylight-docs/out/
|
|
30
|
+
*.tsbuildinfo
|
|
31
|
+
packages/laylight-docs/lib/lyl-samples.generated.ts
|
|
32
|
+
packages/laylight-docs/lib/benchmarks.generated.ts
|
|
33
|
+
packages/laylight-docs/lib/photonics-examples.generated.ts
|
laylight-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: laylight
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Photonic layout in Python, compiled to GDSII by the Laylight compiler
|
|
5
|
+
Keywords: gds,layout,photonics
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: laylight-cli==0.2.2
|
|
8
|
+
Provides-Extra: klayout
|
|
9
|
+
Requires-Dist: klayout>=0.30.8; extra == 'klayout'
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# laylight
|
|
13
|
+
|
|
14
|
+
`laylight` is a Python toolkit for photonic layout design. Devices are Python classes; the library builds the [Laylight IR](https://laylight.photonstack.io/concepts/compilation/) from them, and the Laylight compiler, the `laylight` command line, turns the IR into GDSII.
|
|
15
|
+
|
|
16
|
+
laylight is a sub-product of [PhotonStack](https://photonstack.io/), focused on making photonic design more programmable, composable, and easier to explore.
|
|
17
|
+
|
|
18
|
+
## Why laylight
|
|
19
|
+
|
|
20
|
+
Photonic layout work often mixes device geometry, parameter sweeps, custom scripts, and visualization. laylight provides a Python-first way to:
|
|
21
|
+
|
|
22
|
+
- define photonic structures with reusable, parameterized devices
|
|
23
|
+
- chain devices port to port with `Placer`
|
|
24
|
+
- write layouts that carry their cells' parameters, ports and connections, through the same compiler as the [Laylight DSL](https://laylight.photonstack.io/concepts/dsl/)
|
|
25
|
+
|
|
26
|
+
## Documentation and tools
|
|
27
|
+
|
|
28
|
+
- Documentation: [laylight.photonstack.io](https://laylight.photonstack.io/)
|
|
29
|
+
- Playground: [laylight.photonstack.io/playground](https://laylight.photonstack.io/playground/)
|
|
30
|
+
- VS Code extension: [PhotonStack/laylight-vscode-extension](https://github.com/PhotonStack/laylight-vscode-extension)
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install laylight
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This installs the `laylight` command line too (the `laylight-cli` package). PNG previews and KLayout PCells need KLayout:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install "laylight[klayout]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Simple example
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import laylight
|
|
48
|
+
from laylight.render import new_layout
|
|
49
|
+
from laylight.structure.wg import WGStraight
|
|
50
|
+
|
|
51
|
+
layout, top, tech = new_layout("DEMO")
|
|
52
|
+
|
|
53
|
+
device = WGStraight(
|
|
54
|
+
length=200,
|
|
55
|
+
width=0.5,
|
|
56
|
+
layout=layout,
|
|
57
|
+
tech=tech,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
laylight.Placer(device).into(top)
|
|
61
|
+
layout.write("demo.gds")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This creates a straight waveguide and writes the result to `demo.gds`. `layout.to_ir()` returns the Laylight IR, and `layout.write("demo.lyir")` writes it to a file that Laylight DSL code can `import`.
|
|
65
|
+
|
|
66
|
+
## Learn more
|
|
67
|
+
|
|
68
|
+
For guided examples, API usage, and browser-based experiments, start with the documentation site and the online playground.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# laylight
|
|
2
2
|
|
|
3
|
-
`laylight` is a Python toolkit for photonic layout design.
|
|
3
|
+
`laylight` is a Python toolkit for photonic layout design. Devices are Python classes; the library builds the [Laylight IR](https://laylight.photonstack.io/concepts/compilation/) from them, and the Laylight compiler, the `laylight` command line, turns the IR into GDSII.
|
|
4
4
|
|
|
5
5
|
laylight is a sub-product of [PhotonStack](https://photonstack.io/), focused on making photonic design more programmable, composable, and easier to explore.
|
|
6
6
|
|
|
@@ -8,9 +8,9 @@ laylight is a sub-product of [PhotonStack](https://photonstack.io/), focused on
|
|
|
8
8
|
|
|
9
9
|
Photonic layout work often mixes device geometry, parameter sweeps, custom scripts, and visualization. laylight provides a Python-first way to:
|
|
10
10
|
|
|
11
|
-
- define photonic structures with reusable
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- define photonic structures with reusable, parameterized devices
|
|
12
|
+
- chain devices port to port with `Placer`
|
|
13
|
+
- write layouts that carry their cells' parameters, ports and connections, through the same compiler as the [Laylight DSL](https://laylight.photonstack.io/concepts/dsl/)
|
|
14
14
|
|
|
15
15
|
## Documentation and tools
|
|
16
16
|
|
|
@@ -24,18 +24,20 @@ Photonic layout work often mixes device geometry, parameter sweeps, custom scrip
|
|
|
24
24
|
pip install laylight
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
This installs the `laylight` command line too (the `laylight-cli` package). PNG previews and KLayout PCells need KLayout:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install "laylight[klayout]"
|
|
31
|
+
```
|
|
32
|
+
|
|
27
33
|
## Simple example
|
|
28
34
|
|
|
29
35
|
```python
|
|
30
36
|
import laylight
|
|
31
|
-
|
|
32
|
-
from laylight.technology import Technology
|
|
37
|
+
from laylight.render import new_layout
|
|
33
38
|
from laylight.structure.wg import WGStraight
|
|
34
39
|
|
|
35
|
-
layout =
|
|
36
|
-
layout.dbu = 0.001
|
|
37
|
-
tech = Technology()
|
|
38
|
-
top = layout.create_cell("DEMO")
|
|
40
|
+
layout, top, tech = new_layout("DEMO")
|
|
39
41
|
|
|
40
42
|
device = WGStraight(
|
|
41
43
|
length=200,
|
|
@@ -48,9 +50,8 @@ laylight.Placer(device).into(top)
|
|
|
48
50
|
layout.write("demo.gds")
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
This creates a
|
|
53
|
+
This creates a straight waveguide and writes the result to `demo.gds`. `layout.to_ir()` returns the Laylight IR, and `layout.write("demo.lyir")` writes it to a file that Laylight DSL code can `import`.
|
|
52
54
|
|
|
53
55
|
## Learn more
|
|
54
56
|
|
|
55
57
|
For guided examples, API usage, and browser-based experiments, start with the documentation site and the online playground.
|
|
56
|
-
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""laylight: describe photonic layouts in Python and compile them with the
|
|
2
|
+
Laylight compiler.
|
|
3
|
+
|
|
4
|
+
Devices draw into a `Layout` of cells; `Layout.to_ir()` is the Laylight IR
|
|
5
|
+
of the result and `Layout.write("out.gds")` compiles it to GDSII with the
|
|
6
|
+
`laylight` command line.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from . import structure
|
|
10
|
+
from .backend import CompilerError, CompilerWarning
|
|
11
|
+
from .geometry import Box, Path, Point, Polygon, SimplePolygon, Trans, Vector, text
|
|
12
|
+
from .layout import Cell, CellInstArray, Instance, Layout, Marker, PortRef, read
|
|
13
|
+
from .pcell import (
|
|
14
|
+
PCellParameterSpec,
|
|
15
|
+
abstract_device_to_pcell,
|
|
16
|
+
register_abstract_device_pcell,
|
|
17
|
+
)
|
|
18
|
+
from .placable import Chain, Placer
|
|
19
|
+
from .render import (
|
|
20
|
+
LayoutRenderError,
|
|
21
|
+
RenderArtifacts,
|
|
22
|
+
execute_layout_code,
|
|
23
|
+
export_layout_png,
|
|
24
|
+
new_layout,
|
|
25
|
+
render_code_to_files,
|
|
26
|
+
)
|
|
27
|
+
from .structure.abstract import AbstractDevice, AdhocDevice
|
|
28
|
+
from .technology import LayerInfo, Technology
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"AbstractDevice",
|
|
32
|
+
"AdhocDevice",
|
|
33
|
+
"Box",
|
|
34
|
+
"Cell",
|
|
35
|
+
"CellInstArray",
|
|
36
|
+
"Chain",
|
|
37
|
+
"CompilerError",
|
|
38
|
+
"CompilerWarning",
|
|
39
|
+
"Instance",
|
|
40
|
+
"LayerInfo",
|
|
41
|
+
"Layout",
|
|
42
|
+
"LayoutRenderError",
|
|
43
|
+
"Marker",
|
|
44
|
+
"PCellParameterSpec",
|
|
45
|
+
"Path",
|
|
46
|
+
"Placer",
|
|
47
|
+
"Point",
|
|
48
|
+
"Polygon",
|
|
49
|
+
"PortRef",
|
|
50
|
+
"RenderArtifacts",
|
|
51
|
+
"SimplePolygon",
|
|
52
|
+
"Technology",
|
|
53
|
+
"Trans",
|
|
54
|
+
"Vector",
|
|
55
|
+
"abstract_device_to_pcell",
|
|
56
|
+
"execute_layout_code",
|
|
57
|
+
"export_layout_png",
|
|
58
|
+
"new_layout",
|
|
59
|
+
"read",
|
|
60
|
+
"register_abstract_device_pcell",
|
|
61
|
+
"render_code_to_files",
|
|
62
|
+
"structure",
|
|
63
|
+
"text",
|
|
64
|
+
]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""The compiler backend: the `laylight` command line (the `laylight-cli`
|
|
2
|
+
package) turns Laylight IR into GDSII, and reads GDSII back as IR.
|
|
3
|
+
|
|
4
|
+
The command is found through the `LAYLIGHT_CLI` environment variable, then
|
|
5
|
+
where the installed `laylight-cli` package put it, then next to the running
|
|
6
|
+
Python, then on `PATH`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import functools
|
|
12
|
+
from importlib import metadata
|
|
13
|
+
import os
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
import re
|
|
16
|
+
import shutil
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
import sysconfig
|
|
20
|
+
import tempfile
|
|
21
|
+
from typing import Sequence
|
|
22
|
+
import warnings
|
|
23
|
+
|
|
24
|
+
# The first release whose `build` reads IR.
|
|
25
|
+
MIN_VERSION = (0, 2, 2)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CompilerError(RuntimeError):
|
|
29
|
+
"""The `laylight` command line is missing or failed."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CompilerWarning(UserWarning):
|
|
33
|
+
"""A warning from the `laylight` command line."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def find_cli() -> str:
|
|
37
|
+
"""The path of the `laylight` command."""
|
|
38
|
+
configured = os.environ.get("LAYLIGHT_CLI")
|
|
39
|
+
if configured:
|
|
40
|
+
if not Path(configured).is_file():
|
|
41
|
+
raise CompilerError(f"LAYLIGHT_CLI is set to `{configured}`, which is not a file")
|
|
42
|
+
return configured
|
|
43
|
+
executable = "laylight.exe" if sys.platform == "win32" else "laylight"
|
|
44
|
+
for candidate in _installed_scripts(executable):
|
|
45
|
+
if candidate.is_file():
|
|
46
|
+
return str(candidate)
|
|
47
|
+
for directory in (sysconfig.get_path("scripts"), str(Path(sys.executable).parent)):
|
|
48
|
+
candidate = Path(directory) / executable
|
|
49
|
+
if candidate.is_file():
|
|
50
|
+
return str(candidate)
|
|
51
|
+
found = shutil.which("laylight")
|
|
52
|
+
if found:
|
|
53
|
+
return found
|
|
54
|
+
raise CompilerError(
|
|
55
|
+
"the `laylight` command line was not found: install it with "
|
|
56
|
+
"`pip install laylight-cli`, or point LAYLIGHT_CLI at the executable"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _installed_scripts(executable: str) -> list[Path]:
|
|
61
|
+
"""Where the installed `laylight-cli` distribution put its command: the
|
|
62
|
+
script its record lists, and `bin/` beside it (`pip install --target`)."""
|
|
63
|
+
try:
|
|
64
|
+
dist = metadata.distribution("laylight-cli")
|
|
65
|
+
except metadata.PackageNotFoundError:
|
|
66
|
+
return []
|
|
67
|
+
found = [
|
|
68
|
+
Path(str(dist.locate_file(f)))
|
|
69
|
+
for f in dist.files or []
|
|
70
|
+
if f.name == executable and f.parent.name in ("bin", "Scripts")
|
|
71
|
+
]
|
|
72
|
+
root = Path(str(dist.locate_file("")))
|
|
73
|
+
found += [root / "bin" / executable, root / "Scripts" / executable]
|
|
74
|
+
return found
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@functools.lru_cache(maxsize=None)
|
|
78
|
+
def cli_version(cli: str | None = None) -> tuple[int, ...]:
|
|
79
|
+
"""The version of the `laylight` command, as numbers."""
|
|
80
|
+
command = cli or find_cli()
|
|
81
|
+
result = subprocess.run([command, "--version"], capture_output=True, text=True)
|
|
82
|
+
match = re.search(r"(\d+)\.(\d+)\.(\d+)", result.stdout)
|
|
83
|
+
if result.returncode != 0 or match is None:
|
|
84
|
+
raise CompilerError(f"`{command} --version` failed: {result.stderr.strip() or result.stdout.strip()}")
|
|
85
|
+
return tuple(int(part) for part in match.groups())
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def run(args: Sequence[str], *, cli: str | None = None) -> subprocess.CompletedProcess[str]:
|
|
89
|
+
"""Runs `laylight <args>` and returns the completed process; a failure
|
|
90
|
+
raises `CompilerError` with the command's messages."""
|
|
91
|
+
command = cli or find_cli()
|
|
92
|
+
version = cli_version(command)
|
|
93
|
+
if version < MIN_VERSION:
|
|
94
|
+
needed = ".".join(str(v) for v in MIN_VERSION)
|
|
95
|
+
found = ".".join(str(v) for v in version)
|
|
96
|
+
raise CompilerError(
|
|
97
|
+
f"laylight {found} at `{command}` cannot compile IR; laylight-cli {needed} or later is needed"
|
|
98
|
+
)
|
|
99
|
+
result = subprocess.run([command, *args], capture_output=True, text=True)
|
|
100
|
+
if result.returncode != 0:
|
|
101
|
+
message = result.stderr.strip() or result.stdout.strip()
|
|
102
|
+
raise CompilerError(f"laylight {' '.join(args[:1])} failed:\n{message}")
|
|
103
|
+
for line in result.stderr.splitlines():
|
|
104
|
+
if line.startswith("warning: "):
|
|
105
|
+
warnings.warn(line[len("warning: "):], CompilerWarning, stacklevel=3)
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def build(
|
|
110
|
+
ir_text: str,
|
|
111
|
+
output: "str | os.PathLike[str]",
|
|
112
|
+
*,
|
|
113
|
+
dummy: Sequence[str] = (),
|
|
114
|
+
cli: str | None = None,
|
|
115
|
+
) -> Path:
|
|
116
|
+
"""Compiles IR text to `output` (GDSII, or IR for `.lyir`), with optional
|
|
117
|
+
dummy fill rules (`--dummy`)."""
|
|
118
|
+
target = Path(output).resolve()
|
|
119
|
+
with tempfile.TemporaryDirectory(prefix="laylight-") as tmp:
|
|
120
|
+
source = Path(tmp) / "layout.lyir"
|
|
121
|
+
source.write_text(ir_text, encoding="utf-8")
|
|
122
|
+
args = ["build", str(source), "-o", str(target)]
|
|
123
|
+
for rule in dummy:
|
|
124
|
+
args += ["--dummy", rule]
|
|
125
|
+
run(args, cli=cli)
|
|
126
|
+
return target
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def to_ir(path: "str | os.PathLike[str]", *, cli: str | None = None) -> str:
|
|
130
|
+
"""The IR text of a layout file (GDSII, IR or Laylight source)."""
|
|
131
|
+
return run(["ir", str(Path(path).resolve())], cli=cli).stdout
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
__all__ = [
|
|
135
|
+
"CompilerError",
|
|
136
|
+
"CompilerWarning",
|
|
137
|
+
"MIN_VERSION",
|
|
138
|
+
"build",
|
|
139
|
+
"cli_version",
|
|
140
|
+
"find_cli",
|
|
141
|
+
"run",
|
|
142
|
+
"to_ir",
|
|
143
|
+
]
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""The 5x7 pixel font of the `text` primitive, shared with the Laylight DSL
|
|
2
|
+
(`crates/laylight-dsl/src/geom/font.rs`): printable ASCII.
|
|
3
|
+
|
|
4
|
+
Each glyph lists its pixel rows from the top, `#` for a filled pixel: seven
|
|
5
|
+
rows above the baseline, then optionally two descender rows.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
# Rows above the baseline (the height of a capital letter).
|
|
11
|
+
CAP_ROWS = 7
|
|
12
|
+
# Horizontal distance between characters, in pixels.
|
|
13
|
+
ADVANCE = 6
|
|
14
|
+
# Vertical distance between lines, in pixels.
|
|
15
|
+
LINE_PITCH = 11
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def glyph(c: str) -> str | None:
|
|
19
|
+
index = ord(c) - ord(" ")
|
|
20
|
+
if 0 <= index < len(GLYPHS):
|
|
21
|
+
return GLYPHS[index]
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def boxes(glyph: str) -> list[tuple[int, int, int, int]]:
|
|
26
|
+
"""The pixels of a glyph as few rectangles `(column, row, columns, rows)`,
|
|
27
|
+
`row` counted from the top: horizontal runs, merged with identical runs
|
|
28
|
+
in the rows below."""
|
|
29
|
+
open_boxes: list[tuple[int, int, int, int]] = []
|
|
30
|
+
done: list[tuple[int, int, int, int]] = []
|
|
31
|
+
for row, pixels in enumerate(glyph.split("/")):
|
|
32
|
+
row_runs = _runs(pixels)
|
|
33
|
+
following: list[tuple[int, int, int, int]] = []
|
|
34
|
+
for column, top, columns, rows in open_boxes:
|
|
35
|
+
if (column, columns) in row_runs:
|
|
36
|
+
following.append((column, top, columns, rows + 1))
|
|
37
|
+
else:
|
|
38
|
+
done.append((column, top, columns, rows))
|
|
39
|
+
for column, columns in row_runs:
|
|
40
|
+
if not any(b[0] == column and b[2] == columns for b in following):
|
|
41
|
+
following.append((column, row, columns, 1))
|
|
42
|
+
open_boxes = following
|
|
43
|
+
done.extend(open_boxes)
|
|
44
|
+
return done
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _runs(pixels: str) -> list[tuple[int, int]]:
|
|
48
|
+
"""`(start, length)` of each run of filled pixels."""
|
|
49
|
+
out: list[tuple[int, int]] = []
|
|
50
|
+
start = None
|
|
51
|
+
for i, p in enumerate(pixels + "."):
|
|
52
|
+
if p == "#" and start is None:
|
|
53
|
+
start = i
|
|
54
|
+
elif p != "#" and start is not None:
|
|
55
|
+
out.append((start, i - start))
|
|
56
|
+
start = None
|
|
57
|
+
return out
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Glyphs for U+0020 to U+007E.
|
|
61
|
+
GLYPHS = (
|
|
62
|
+
"...../...../...../...../...../...../.....", # space
|
|
63
|
+
"..#../..#../..#../..#../..#../...../..#..", # !
|
|
64
|
+
".#.#./.#.#./.#.#./...../...../...../.....", # "
|
|
65
|
+
".#.#./.#.#./#####/.#.#./#####/.#.#./.#.#.", # #
|
|
66
|
+
"..#../.####/#.#../.###./..#.#/####./..#..", # $
|
|
67
|
+
"##.../##..#/...#./..#../.#.../#..##/...##", # %
|
|
68
|
+
".##../#..#./#.#../.#.../#.#.#/#..#./.##.#", # &
|
|
69
|
+
"..#../..#../...../...../...../...../.....", # '
|
|
70
|
+
"...#./..#../.#.../.#.../.#.../..#../...#.", # (
|
|
71
|
+
".#.../..#../...#./...#./...#./..#../.#...", # )
|
|
72
|
+
"...../..#../#.#.#/.###./#.#.#/..#../.....", # *
|
|
73
|
+
"...../..#../..#../#####/..#../..#../.....", # +
|
|
74
|
+
"...../...../...../...../...../.##../.##../..#../.#...", # ,
|
|
75
|
+
"...../...../...../#####/...../...../.....", # -
|
|
76
|
+
"...../...../...../...../...../.##../.##..", # .
|
|
77
|
+
"...../....#/...#./..#../.#.../#..../.....", # /
|
|
78
|
+
".###./#...#/#..##/#.#.#/##..#/#...#/.###.", # 0
|
|
79
|
+
"..#../.##../..#../..#../..#../..#../.###.", # 1
|
|
80
|
+
".###./#...#/....#/...#./..#../.#.../#####", # 2
|
|
81
|
+
"#####/...#./..#../...#./....#/#...#/.###.", # 3
|
|
82
|
+
"...#./..##./.#.#./#..#./#####/...#./...#.", # 4
|
|
83
|
+
"#####/#..../####./....#/....#/#...#/.###.", # 5
|
|
84
|
+
"..##./.#.../#..../####./#...#/#...#/.###.", # 6
|
|
85
|
+
"#####/....#/...#./..#../.#.../.#.../.#...", # 7
|
|
86
|
+
".###./#...#/#...#/.###./#...#/#...#/.###.", # 8
|
|
87
|
+
".###./#...#/#...#/.####/....#/...#./.##..", # 9
|
|
88
|
+
"...../.##../.##../...../...../.##../.##..", # :
|
|
89
|
+
"...../.##../.##../...../...../.##../.##../..#../.#...", # ;
|
|
90
|
+
"...#./..#../.#.../#..../.#.../..#../...#.", # <
|
|
91
|
+
"...../...../#####/...../#####/...../.....", # =
|
|
92
|
+
".#.../..#../...#./....#/...#./..#../.#...", # >
|
|
93
|
+
".###./#...#/....#/...#./..#../...../..#..", # ?
|
|
94
|
+
".###./#...#/....#/.##.#/#.#.#/#.#.#/.###.", # @
|
|
95
|
+
".###./#...#/#...#/#####/#...#/#...#/#...#", # A
|
|
96
|
+
"####./#...#/#...#/####./#...#/#...#/####.", # B
|
|
97
|
+
".###./#...#/#..../#..../#..../#...#/.###.", # C
|
|
98
|
+
"###../#..#./#...#/#...#/#...#/#..#./###..", # D
|
|
99
|
+
"#####/#..../#..../####./#..../#..../#####", # E
|
|
100
|
+
"#####/#..../#..../####./#..../#..../#....", # F
|
|
101
|
+
".###./#...#/#..../#.###/#...#/#...#/.####", # G
|
|
102
|
+
"#...#/#...#/#...#/#####/#...#/#...#/#...#", # H
|
|
103
|
+
".###./..#../..#../..#../..#../..#../.###.", # I
|
|
104
|
+
"..###/...#./...#./...#./...#./#..#./.##..", # J
|
|
105
|
+
"#...#/#..#./#.#../##.../#.#../#..#./#...#", # K
|
|
106
|
+
"#..../#..../#..../#..../#..../#..../#####", # L
|
|
107
|
+
"#...#/##.##/#.#.#/#.#.#/#...#/#...#/#...#", # M
|
|
108
|
+
"#...#/#...#/##..#/#.#.#/#..##/#...#/#...#", # N
|
|
109
|
+
".###./#...#/#...#/#...#/#...#/#...#/.###.", # O
|
|
110
|
+
"####./#...#/#...#/####./#..../#..../#....", # P
|
|
111
|
+
".###./#...#/#...#/#...#/#.#.#/#..#./.##.#", # Q
|
|
112
|
+
"####./#...#/#...#/####./#.#../#..#./#...#", # R
|
|
113
|
+
".####/#..../#..../.###./....#/....#/####.", # S
|
|
114
|
+
"#####/..#../..#../..#../..#../..#../..#..", # T
|
|
115
|
+
"#...#/#...#/#...#/#...#/#...#/#...#/.###.", # U
|
|
116
|
+
"#...#/#...#/#...#/#...#/#...#/.#.#./..#..", # V
|
|
117
|
+
"#...#/#...#/#...#/#.#.#/#.#.#/#.#.#/.#.#.", # W
|
|
118
|
+
"#...#/#...#/.#.#./..#../.#.#./#...#/#...#", # X
|
|
119
|
+
"#...#/#...#/.#.#./..#../..#../..#../..#..", # Y
|
|
120
|
+
"#####/....#/...#./..#../.#.../#..../#####", # Z
|
|
121
|
+
".###./.#.../.#.../.#.../.#.../.#.../.###.", # [
|
|
122
|
+
"...../#..../.#.../..#../...#./....#/.....", # backslash
|
|
123
|
+
".###./...#./...#./...#./...#./...#./.###.", # ]
|
|
124
|
+
"..#../.#.#./#...#/...../...../...../.....", # ^
|
|
125
|
+
"...../...../...../...../...../...../#####", # _
|
|
126
|
+
".#.../..#../...../...../...../...../.....", # `
|
|
127
|
+
"...../...../.###./....#/.####/#...#/.####", # a
|
|
128
|
+
"#..../#..../#.##./##..#/#...#/#...#/####.", # b
|
|
129
|
+
"...../...../.###./#..../#..../#...#/.###.", # c
|
|
130
|
+
"....#/....#/.##.#/#..##/#...#/#...#/.####", # d
|
|
131
|
+
"...../...../.###./#...#/#####/#..../.###.", # e
|
|
132
|
+
"..##./.#..#/.#.../###../.#.../.#.../.#...", # f
|
|
133
|
+
"...../...../.####/#...#/#...#/#...#/.####/....#/.###.", # g
|
|
134
|
+
"#..../#..../#.##./##..#/#...#/#...#/#...#", # h
|
|
135
|
+
"..#../...../.##../..#../..#../..#../.###.", # i
|
|
136
|
+
"...#./...../..##./...#./...#./...#./...#./#..#./.##..", # j
|
|
137
|
+
"#..../#..../#..#./#.#../##.../#.#../#..#.", # k
|
|
138
|
+
".##../..#../..#../..#../..#../..#../.###.", # l
|
|
139
|
+
"...../...../##.#./#.#.#/#.#.#/#.#.#/#.#.#", # m
|
|
140
|
+
"...../...../#.##./##..#/#...#/#...#/#...#", # n
|
|
141
|
+
"...../...../.###./#...#/#...#/#...#/.###.", # o
|
|
142
|
+
"...../...../####./#...#/#...#/#...#/####./#..../#....", # p
|
|
143
|
+
"...../...../.####/#...#/#...#/#...#/.####/....#/....#", # q
|
|
144
|
+
"...../...../#.##./##..#/#..../#..../#....", # r
|
|
145
|
+
"...../...../.####/#..../.###./....#/####.", # s
|
|
146
|
+
".#.../.#.../###../.#.../.#.../.#..#/..##.", # t
|
|
147
|
+
"...../...../#...#/#...#/#...#/#..##/.##.#", # u
|
|
148
|
+
"...../...../#...#/#...#/#...#/.#.#./..#..", # v
|
|
149
|
+
"...../...../#...#/#...#/#.#.#/#.#.#/.#.#.", # w
|
|
150
|
+
"...../...../#...#/.#.#./..#../.#.#./#...#", # x
|
|
151
|
+
"...../...../#...#/#...#/#...#/#...#/.####/....#/.###.", # y
|
|
152
|
+
"...../...../#####/...#./..#../.#.../#####", # z
|
|
153
|
+
"...#./..#../..#../.#.../..#../..#../...#.", # {
|
|
154
|
+
"..#../..#../..#../..#../..#../..#../..#..", # |
|
|
155
|
+
".#.../..#../..#../...#./..#../..#../.#...", # }
|
|
156
|
+
"...../...../.#.../#.#.#/...#./...../.....", # ~
|
|
157
|
+
)
|