Skip to content

Commit 695c013

Browse files
authored
fix: partial backport of #648 (#670)
This just fixes the issue where substituting_steps would be applied to all flows, disallowing the use of flows like OpenInKLayout or OpenInOpenROAD. The breaking strict class-level substitution AND the re-numbering will both remain in 3.0.0.
1 parent 1bbc4f7 commit 695c013

File tree

4 files changed

+56
-26
lines changed

4 files changed

+56
-26
lines changed

Changelog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
## Documentation
1515
-->
1616

17+
# 2.3.8
18+
19+
## Misc. Enhancements/Bugfixes
20+
21+
* Fixed substitutions in `config.json` being applied to all flows. It now only
22+
applies to the flow in meta.flow (which falls back to `Classic` if it's null.)
23+
1724
# 2.3.7
1825

1926
## Tool Updates

openlane/__main__.py

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import subprocess
2222
from textwrap import dedent
2323
from functools import partial
24-
from typing import Any, Dict, Sequence, Tuple, Type, Optional, List, Union
24+
from typing import Any, Dict, Sequence, Tuple, Type, Optional, List
2525

2626
import click
2727
from cloup import (
@@ -77,21 +77,39 @@ def run(
7777
err("No config file(s) have been provided.")
7878
ctx.exit(1)
7979

80-
flow_description: Optional[Union[str, List[str]]] = None
81-
substitutions: Union[None, Dict[str, Union[str, None]]] = None
80+
TargetFlow: Optional[Type[Flow]] = Flow.factory.get("Classic")
8281

8382
for config_file in config_files:
8483
if meta := Config.get_meta(config_file):
85-
if meta.flow is not None:
86-
flow_description = meta.flow
87-
if meta.substituting_steps is not None:
88-
substitutions = meta.substituting_steps
84+
# to maintain backwards compat, in 3 you will need to explicitly
85+
# set the flow you're substituting
86+
target_flow_desc = meta.flow or "Classic"
87+
88+
if isinstance(target_flow_desc, str):
89+
if found := Flow.factory.get(target_flow_desc):
90+
TargetFlow = found
91+
else:
92+
err(
93+
f"Unknown flow '{meta.flow}' specified in configuration file's 'meta' object."
94+
)
95+
ctx.exit(1)
96+
elif isinstance(target_flow_desc, list):
97+
TargetFlow = SequentialFlow.make(target_flow_desc)
98+
if meta.substituting_steps is not None and issubclass(
99+
TargetFlow, SequentialFlow
100+
):
101+
if meta.flow is None:
102+
warn(
103+
'config_file currently has substituting_steps set with no flow, where it will fall back to Classic. Starting OpenLane 3.0.0, this will be an error. Please update your configuration to explicitly set "flow" to "Classic".'
104+
)
105+
TargetFlow = TargetFlow.Substitute(meta.substituting_steps) # type: ignore # Type checker is being rowdy with this one
89106

90107
if flow_name is not None:
91-
flow_description = flow_name
92-
93-
if flow_description is None:
94-
flow_description = "Classic"
108+
if found := Flow.factory.get(flow_name):
109+
TargetFlow = found
110+
else:
111+
err(f"Unknown flow '{flow_name}' passed to initialization function.")
112+
ctx.exit(1)
95113

96114
if len(initial_state_element_override):
97115
if with_initial_state is None:
@@ -114,18 +132,9 @@ def run(
114132
overrides=overrides,
115133
)
116134

117-
TargetFlow: Type[Flow]
118-
119-
if isinstance(flow_description, str):
120-
if FlowClass := Flow.factory.get(flow_description):
121-
TargetFlow = FlowClass
122-
else:
123-
err(
124-
f"Unknown flow '{flow_description}' specified in configuration file's 'meta' object."
125-
)
126-
ctx.exit(1)
127-
else:
128-
TargetFlow = SequentialFlow.make(flow_description)
135+
assert (
136+
TargetFlow is not None
137+
), "TargetFlow is unexpectedly None. Please report this as a bug."
129138

130139
kwargs: Dict[str, Any] = {
131140
"pdk_root": pdk_root,
@@ -134,8 +143,6 @@ def run(
134143
"config_override_strings": config_override_strings,
135144
"design_dir": design_dir,
136145
}
137-
if issubclass(TargetFlow, SequentialFlow):
138-
kwargs["Substitute"] = substitutions
139146
flow = TargetFlow(config_files, **kwargs)
140147
except PassedDirectoryError as e:
141148
err(e)

openlane/flows/sequential.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
DeferredStepError,
4040
)
4141

42+
Substitution = Union[str, Type[Step], None]
43+
4244

4345
class SequentialFlow(Flow):
4446
"""
@@ -120,6 +122,20 @@ class CustomSequentialFlow(SequentialFlow):
120122

121123
return CustomSequentialFlow
122124

125+
@classmethod
126+
def Substitute(
127+
Self, Substitutions: Dict[str, Substitution]
128+
) -> Type[SequentialFlow]:
129+
"""
130+
Convenience method to quickly subclass a sequential flow and add
131+
Substitutions to it.
132+
133+
The new flow shall be named ``{previous_flow_name}'``.
134+
135+
:param Substitutions: The substitutions to use for the new subclass.
136+
"""
137+
return type(Self.__name__ + "'", (Self,), {"Substitutions": Substitutions})
138+
123139
def __init__(
124140
self,
125141
*args,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "openlane"
3-
version = "2.3.7"
3+
version = "2.3.8"
44
description = "An infrastructure for implementing chip design flows"
55
authors = ["Efabless Corporation and Contributors <[email protected]>"]
66
readme = "Readme.md"

0 commit comments

Comments
 (0)