tlaplus-cli 0.2.1__tar.gz → 0.3.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.
- {tlaplus_cli-0.2.1/src/tlaplus_cli.egg-info → tlaplus_cli-0.3.2}/PKG-INFO +67 -30
- tlaplus_cli-0.3.2/README.md +218 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/pyproject.toml +3 -1
- tlaplus_cli-0.3.2/src/tlaplus_cli/__init__.py +6 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/build_tlc_module.py +13 -14
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/cli.py +9 -6
- tlaplus_cli-0.3.2/src/tlaplus_cli/project.py +35 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/run_tlc.py +38 -19
- tlaplus_cli-0.2.1/src/tlaplus_cli/tlc_manager.py → tlaplus_cli-0.3.2/src/tlaplus_cli/tools_manager.py +130 -76
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/version_manager.py +95 -20
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2/src/tlaplus_cli.egg-info}/PKG-INFO +67 -30
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli.egg-info/SOURCES.txt +5 -2
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli.egg-info/requires.txt +1 -0
- tlaplus_cli-0.3.2/tests/test_build_tlc_module.py +161 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/tests/test_java_module_naming.py +8 -7
- tlaplus_cli-0.3.2/tests/test_project.py +46 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/tests/test_run_tlc.py +10 -10
- tlaplus_cli-0.3.2/tests/test_spec_resolution.py +200 -0
- tlaplus_cli-0.3.2/tests/test_tools_manager.py +636 -0
- tlaplus_cli-0.3.2/tests/test_version_manager.py +193 -0
- tlaplus_cli-0.2.1/README.md +0 -182
- tlaplus_cli-0.2.1/src/tlaplus_cli/__init__.py +0 -0
- tlaplus_cli-0.2.1/tests/test_build_tlc_module.py +0 -77
- tlaplus_cli-0.2.1/tests/test_tlc_manager.py +0 -355
- tlaplus_cli-0.2.1/tests/test_version_manager.py +0 -70
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/LICENSE +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/MANIFEST.in +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/setup.cfg +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/check_java.py +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/config.py +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/py.typed +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/resources/default_config.yaml +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli/settings.py +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli.egg-info/dependency_links.txt +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli.egg-info/entry_points.txt +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/src/tlaplus_cli.egg-info/top_level.txt +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/tests/test_check_java.py +0 -0
- {tlaplus_cli-0.2.1 → tlaplus_cli-0.3.2}/tests/test_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tlaplus-cli
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: TLA+ tools: download TLC, compile custom modules, run model checker
|
|
5
5
|
Author-email: Denis Nikolskiy <codeomatics@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,6 +24,7 @@ Requires-Dist: requests<3.0.0,>=2.31.0
|
|
|
24
24
|
Requires-Dist: pyyaml<7.0,>=6.0
|
|
25
25
|
Requires-Dist: platformdirs<5.0,>=4.0
|
|
26
26
|
Requires-Dist: pydantic<3.0.0,>=2.12.5
|
|
27
|
+
Requires-Dist: truststore<1.0.0,>=0.9.0
|
|
27
28
|
Dynamic: license-file
|
|
28
29
|
|
|
29
30
|
# TLA+ CLI
|
|
@@ -50,99 +51,135 @@ uv tool uninstall tlaplus-cli
|
|
|
50
51
|
|
|
51
52
|
## Usage
|
|
52
53
|
|
|
53
|
-
### Managing
|
|
54
|
+
### Managing TLA+ Tools
|
|
54
55
|
|
|
55
|
-
The `tla
|
|
56
|
+
The `tla tools` command group allows you to download and manage multiple versions of the TLA+ toolset (TLC, SANY, TLATeX) directly from GitHub releases.
|
|
56
57
|
|
|
57
|
-
List available and installed
|
|
58
|
+
List available and installed toolset versions:
|
|
58
59
|
```bash
|
|
59
|
-
tla
|
|
60
|
+
tla tools list
|
|
60
61
|
```
|
|
61
62
|
|
|
62
|
-
Install the latest
|
|
63
|
+
Install the latest toolset version:
|
|
63
64
|
```bash
|
|
64
|
-
tla
|
|
65
|
+
tla tools install
|
|
65
66
|
```
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
> [!TIP]
|
|
69
|
+
> The first version you install is automatically "pinned" as the default. Subsequent installs won't change your pin unless you manually use `tla tools pin`.
|
|
70
|
+
|
|
71
|
+
Install a specific toolset version:
|
|
68
72
|
```bash
|
|
69
|
-
tla
|
|
73
|
+
tla tools install v1.8.0
|
|
70
74
|
```
|
|
71
75
|
|
|
72
76
|
Pin a specific version to be used by default:
|
|
73
77
|
```bash
|
|
74
|
-
tla
|
|
78
|
+
tla tools pin v1.8.0
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
Upgrade the pinned version (or a specific version) to a newer commit:
|
|
78
82
|
```bash
|
|
79
|
-
tla
|
|
83
|
+
tla tools upgrade
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
> [!NOTE]
|
|
87
|
+
> If the target version to upgrade is not yet installed locally, the CLI will automatically download it.
|
|
88
|
+
|
|
89
|
+
Show the absolute path to the pinned version's `tla2tools.jar`:
|
|
83
90
|
```bash
|
|
84
|
-
tla
|
|
91
|
+
tla tools path
|
|
85
92
|
```
|
|
86
93
|
|
|
87
94
|
Or for a specific version:
|
|
88
95
|
```bash
|
|
89
|
-
tla
|
|
96
|
+
tla tools path v1.8.0
|
|
90
97
|
```
|
|
91
98
|
|
|
92
99
|
Example output:
|
|
93
100
|
```text
|
|
94
|
-
|
|
95
|
-
/home/bob/.cache/tla/tlc/v1.8.0-5a47802/tla2tools.jar
|
|
101
|
+
/home/bob/.cache/tla/tools/v1.8.0-5a47802/tla2tools.jar
|
|
96
102
|
```
|
|
97
103
|
|
|
98
|
-
Show the
|
|
104
|
+
Show the toolset versions directory and all installed version directories:
|
|
99
105
|
```bash
|
|
100
|
-
tla
|
|
106
|
+
tla tools dir
|
|
101
107
|
```
|
|
102
108
|
|
|
103
109
|
Example output:
|
|
104
110
|
```text
|
|
105
|
-
/home/bob/.cache/tla/
|
|
111
|
+
/home/bob/.cache/tla/tools
|
|
106
112
|
v1.7.0-abc1234
|
|
107
113
|
v1.8.0-5a47802
|
|
108
114
|
```
|
|
109
115
|
|
|
110
116
|
Uninstall a specific version (or use 'default' to remove legacy jars):
|
|
111
117
|
```bash
|
|
112
|
-
tla
|
|
118
|
+
tla tools uninstall v1.8.0
|
|
113
119
|
```
|
|
114
120
|
|
|
121
|
+
> [!TIP]
|
|
122
|
+
> Use `--all` to remove all installed tags for a specific version name without interactive prompts.
|
|
123
|
+
|
|
124
|
+
> [!NOTE]
|
|
125
|
+
> If you uninstall the currently pinned version, the CLI will automatically "fall back" to the next best installed version (ranked by semver, then release date).
|
|
126
|
+
|
|
115
127
|
### Run TLC
|
|
116
128
|
|
|
117
|
-
Run the TLC model checker on a specification. This uses the currently pinned
|
|
129
|
+
Run the TLC model checker on a specification. This uses the currently pinned toolset version.
|
|
118
130
|
|
|
119
131
|
```bash
|
|
120
|
-
tla
|
|
132
|
+
tla tlc <spec_name>
|
|
121
133
|
```
|
|
122
134
|
|
|
123
135
|
For example (runs `queue.tla`):
|
|
124
136
|
|
|
125
137
|
```bash
|
|
126
|
-
tla
|
|
138
|
+
tla tlc queue
|
|
127
139
|
```
|
|
128
140
|
|
|
129
|
-
|
|
141
|
+
#### Project-Aware Execution
|
|
142
|
+
|
|
143
|
+
The CLI intelligently resolves the specification file and its project structure. It will check the following locations:
|
|
144
|
+
1. `<spec_name>` (if it's a direct path to a file, e.g., `models/my_model.tla`)
|
|
145
|
+
2. `<spec_name>.tla`
|
|
146
|
+
3. `spec/<spec_name>.tla` (checks an inner `spec/` folder relative to the spec's location)
|
|
147
|
+
|
|
148
|
+
Furthermore, it automatically discovers the **project root** by looking for `modules/`, `classes/`, or `lib/` directories adjacent to the spec file or its parent. When a project root is found:
|
|
149
|
+
- The project's `classes/` directory is added to the Java classpath.
|
|
150
|
+
- Any `*.jar` files found in the project's `lib/` directory are added to the Java classpath.
|
|
151
|
+
- The `-DTLA-Library` system property is set to the project's `modules/` directory, allowing TLC to find your custom Java overrides.
|
|
152
|
+
|
|
153
|
+
To check the currently pinned `tla2tools.jar` path and its TLC version:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
tla tlc --version
|
|
157
|
+
```
|
|
130
158
|
|
|
131
159
|
### Compile Custom Java Modules
|
|
132
160
|
|
|
133
|
-
|
|
161
|
+
Java modules (overrides) are compiled using the pinned version of the toolset.
|
|
162
|
+
|
|
163
|
+
Compile modules in the current project:
|
|
164
|
+
```bash
|
|
165
|
+
tla modules build
|
|
166
|
+
```
|
|
134
167
|
|
|
135
|
-
Compile modules:
|
|
168
|
+
Compile modules in a specific project path:
|
|
136
169
|
```bash
|
|
137
|
-
tla build
|
|
170
|
+
tla modules build path/to/project
|
|
138
171
|
```
|
|
139
172
|
|
|
140
173
|
Verbose output:
|
|
141
174
|
```bash
|
|
142
|
-
tla build --verbose
|
|
175
|
+
tla modules build --verbose
|
|
143
176
|
```
|
|
144
177
|
|
|
145
|
-
|
|
178
|
+
The build command:
|
|
179
|
+
1. Resolves the project root (defaults to `workspace.root` from config).
|
|
180
|
+
2. Includes `lib/*.jar` files from the project root in the `javac` classpath.
|
|
181
|
+
3. Compiles `.java` files from the project's `modules/` directory into its `classes/` directory.
|
|
182
|
+
4. Generates the necessary Java service provider configuration for TLC overrides.
|
|
146
183
|
|
|
147
184
|
### Check Java Version
|
|
148
185
|
|
|
@@ -196,7 +233,7 @@ java:
|
|
|
196
233
|
| Directory | Purpose | Location |
|
|
197
234
|
|---|---|---|
|
|
198
235
|
| Config | `config.yaml` | `~/.config/tla/` |
|
|
199
|
-
|
|
|
236
|
+
| Toolset Versions | Version dirs & `tools-pinned-version.txt` file | `~/.cache/tla/tools/` |
|
|
200
237
|
| API Cache | `github_cache.json` | `~/.cache/tla/` |
|
|
201
238
|
| Workspace | specs + modules + classes | Set via `workspace.root` in config |
|
|
202
239
|
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# TLA+ CLI
|
|
2
|
+
|
|
3
|
+
Command-line tool for working with TLA+ specifications and the TLC model checker.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install system-wide via uv tool:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv tool install tlaplus-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Upgrade:
|
|
14
|
+
```bash
|
|
15
|
+
uv tool upgrade tlaplus-cli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Uninstall:
|
|
19
|
+
```bash
|
|
20
|
+
uv tool uninstall tlaplus-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Managing TLA+ Tools
|
|
26
|
+
|
|
27
|
+
The `tla tools` command group allows you to download and manage multiple versions of the TLA+ toolset (TLC, SANY, TLATeX) directly from GitHub releases.
|
|
28
|
+
|
|
29
|
+
List available and installed toolset versions:
|
|
30
|
+
```bash
|
|
31
|
+
tla tools list
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Install the latest toolset version:
|
|
35
|
+
```bash
|
|
36
|
+
tla tools install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> [!TIP]
|
|
40
|
+
> The first version you install is automatically "pinned" as the default. Subsequent installs won't change your pin unless you manually use `tla tools pin`.
|
|
41
|
+
|
|
42
|
+
Install a specific toolset version:
|
|
43
|
+
```bash
|
|
44
|
+
tla tools install v1.8.0
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Pin a specific version to be used by default:
|
|
48
|
+
```bash
|
|
49
|
+
tla tools pin v1.8.0
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Upgrade the pinned version (or a specific version) to a newer commit:
|
|
53
|
+
```bash
|
|
54
|
+
tla tools upgrade
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> [!NOTE]
|
|
58
|
+
> If the target version to upgrade is not yet installed locally, the CLI will automatically download it.
|
|
59
|
+
|
|
60
|
+
Show the absolute path to the pinned version's `tla2tools.jar`:
|
|
61
|
+
```bash
|
|
62
|
+
tla tools path
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or for a specific version:
|
|
66
|
+
```bash
|
|
67
|
+
tla tools path v1.8.0
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Example output:
|
|
71
|
+
```text
|
|
72
|
+
/home/bob/.cache/tla/tools/v1.8.0-5a47802/tla2tools.jar
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Show the toolset versions directory and all installed version directories:
|
|
76
|
+
```bash
|
|
77
|
+
tla tools dir
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Example output:
|
|
81
|
+
```text
|
|
82
|
+
/home/bob/.cache/tla/tools
|
|
83
|
+
v1.7.0-abc1234
|
|
84
|
+
v1.8.0-5a47802
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Uninstall a specific version (or use 'default' to remove legacy jars):
|
|
88
|
+
```bash
|
|
89
|
+
tla tools uninstall v1.8.0
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> [!TIP]
|
|
93
|
+
> Use `--all` to remove all installed tags for a specific version name without interactive prompts.
|
|
94
|
+
|
|
95
|
+
> [!NOTE]
|
|
96
|
+
> If you uninstall the currently pinned version, the CLI will automatically "fall back" to the next best installed version (ranked by semver, then release date).
|
|
97
|
+
|
|
98
|
+
### Run TLC
|
|
99
|
+
|
|
100
|
+
Run the TLC model checker on a specification. This uses the currently pinned toolset version.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
tla tlc <spec_name>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For example (runs `queue.tla`):
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
tla tlc queue
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Project-Aware Execution
|
|
113
|
+
|
|
114
|
+
The CLI intelligently resolves the specification file and its project structure. It will check the following locations:
|
|
115
|
+
1. `<spec_name>` (if it's a direct path to a file, e.g., `models/my_model.tla`)
|
|
116
|
+
2. `<spec_name>.tla`
|
|
117
|
+
3. `spec/<spec_name>.tla` (checks an inner `spec/` folder relative to the spec's location)
|
|
118
|
+
|
|
119
|
+
Furthermore, it automatically discovers the **project root** by looking for `modules/`, `classes/`, or `lib/` directories adjacent to the spec file or its parent. When a project root is found:
|
|
120
|
+
- The project's `classes/` directory is added to the Java classpath.
|
|
121
|
+
- Any `*.jar` files found in the project's `lib/` directory are added to the Java classpath.
|
|
122
|
+
- The `-DTLA-Library` system property is set to the project's `modules/` directory, allowing TLC to find your custom Java overrides.
|
|
123
|
+
|
|
124
|
+
To check the currently pinned `tla2tools.jar` path and its TLC version:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
tla tlc --version
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Compile Custom Java Modules
|
|
131
|
+
|
|
132
|
+
Java modules (overrides) are compiled using the pinned version of the toolset.
|
|
133
|
+
|
|
134
|
+
Compile modules in the current project:
|
|
135
|
+
```bash
|
|
136
|
+
tla modules build
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Compile modules in a specific project path:
|
|
140
|
+
```bash
|
|
141
|
+
tla modules build path/to/project
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Verbose output:
|
|
145
|
+
```bash
|
|
146
|
+
tla modules build --verbose
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The build command:
|
|
150
|
+
1. Resolves the project root (defaults to `workspace.root` from config).
|
|
151
|
+
2. Includes `lib/*.jar` files from the project root in the `javac` classpath.
|
|
152
|
+
3. Compiles `.java` files from the project's `modules/` directory into its `classes/` directory.
|
|
153
|
+
4. Generates the necessary Java service provider configuration for TLC overrides.
|
|
154
|
+
|
|
155
|
+
### Check Java Version
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
tla check-java
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Cache Management
|
|
162
|
+
|
|
163
|
+
The CLI caches GitHub API responses for 1 hour to prevent rate limiting. To clear this cache manually:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
tla fetch-cache clear
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Configuration
|
|
170
|
+
|
|
171
|
+
On first run, a default config is created at:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
~/.config/tla/config.yaml
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Edit this file to set your workspace path and TLC options:
|
|
178
|
+
|
|
179
|
+
```yaml
|
|
180
|
+
tla:
|
|
181
|
+
urls:
|
|
182
|
+
tags: https://api.github.com/repos/tlaplus/tlaplus/tags
|
|
183
|
+
releases: https://api.github.com/repos/tlaplus/tlaplus/releases
|
|
184
|
+
|
|
185
|
+
workspace:
|
|
186
|
+
root: . # Project root (relative to CWD)
|
|
187
|
+
spec_dir: spec # Directory containing .tla files
|
|
188
|
+
modules_dir: modules # Directory containing .java files
|
|
189
|
+
classes_dir: classes # Directory for compiled .class files
|
|
190
|
+
|
|
191
|
+
tlc:
|
|
192
|
+
java_class: tlc2.TLC
|
|
193
|
+
overrides_class: tlc2.overrides.TLCOverrides
|
|
194
|
+
|
|
195
|
+
java:
|
|
196
|
+
min_version: 11
|
|
197
|
+
opts:
|
|
198
|
+
- "-XX:+IgnoreUnrecognizedVMOptions"
|
|
199
|
+
- "-XX:+UseParallelGC"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Directory Layout
|
|
203
|
+
|
|
204
|
+
| Directory | Purpose | Location |
|
|
205
|
+
|---|---|---|
|
|
206
|
+
| Config | `config.yaml` | `~/.config/tla/` |
|
|
207
|
+
| Toolset Versions | Version dirs & `tools-pinned-version.txt` file | `~/.cache/tla/tools/` |
|
|
208
|
+
| API Cache | `github_cache.json` | `~/.cache/tla/` |
|
|
209
|
+
| Workspace | specs + modules + classes | Set via `workspace.root` in config |
|
|
210
|
+
|
|
211
|
+
## Note on Package Name
|
|
212
|
+
|
|
213
|
+
This package is distributed on PyPI as **`tlaplus-cli`** but imports as **`tla`**. There is a separate, unrelated [`tla`](https://pypi.org/project/tla/) package on PyPI (a TLA+ parser). If you have both installed, they will conflict. In practice this is unlikely since they serve different purposes, but be aware of it.
|
|
214
|
+
|
|
215
|
+
## Dependencies
|
|
216
|
+
|
|
217
|
+
* **Java >= 11**: Required for TLC.
|
|
218
|
+
* [**uv**](https://docs.astral.sh/uv/getting-started/installation/): For installing the tool.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tlaplus-cli"
|
|
7
|
-
version = "0.2
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
description = "TLA+ tools: download TLC, compile custom modules, run model checker"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -14,6 +14,7 @@ dependencies = [
|
|
|
14
14
|
"pyyaml>=6.0,<7.0",
|
|
15
15
|
"platformdirs>=4.0,<5.0",
|
|
16
16
|
"pydantic>=2.12.5,<3.0.0",
|
|
17
|
+
"truststore>=0.9.0,<1.0.0",
|
|
17
18
|
]
|
|
18
19
|
authors = [
|
|
19
20
|
{name = "Denis Nikolskiy", email = "codeomatics@gmail.com"}
|
|
@@ -100,6 +101,7 @@ ignore = [
|
|
|
100
101
|
[tool.ruff.lint.per-file-ignores]
|
|
101
102
|
"tests/*" = [
|
|
102
103
|
"ARG001", # Unused arguments are common in tests (fixtures)
|
|
104
|
+
"PLR0913", # Many arguments are common in tests (fixtures)
|
|
103
105
|
]
|
|
104
106
|
|
|
105
107
|
[tool.poe.tasks]
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Compile custom TLC modules (Java)."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
4
6
|
|
|
5
7
|
import typer
|
|
6
8
|
|
|
@@ -9,10 +11,12 @@ from tlaplus_cli.version_manager import get_pinned_version_dir
|
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
def build(
|
|
14
|
+
path: str | None = typer.Argument(None, help="Project root directory (defaults to workspace root)."),
|
|
12
15
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show compilation output."),
|
|
13
16
|
) -> None:
|
|
14
17
|
"""Compile custom Java modules."""
|
|
15
18
|
config = load_config()
|
|
19
|
+
base_dir = Path(path).resolve() if path is not None else workspace_root()
|
|
16
20
|
|
|
17
21
|
# Fallback chain: pinned directory -> legacy jar
|
|
18
22
|
pinned_dir = get_pinned_version_dir()
|
|
@@ -20,31 +24,29 @@ def build(
|
|
|
20
24
|
legacy = cache_dir() / "tla2tools.jar"
|
|
21
25
|
jar_path = pinned_jar if (pinned_jar and pinned_jar.exists()) else legacy
|
|
22
26
|
|
|
23
|
-
ws_root = workspace_root()
|
|
24
|
-
modules_dir = ws_root / config.workspace.modules_dir
|
|
25
|
-
classes_dir = ws_root / config.workspace.classes_dir
|
|
26
|
-
|
|
27
27
|
if not jar_path.exists():
|
|
28
|
-
typer.echo("Error: tla2tools.jar not found.", err=True)
|
|
29
|
-
typer.echo("Run 'tla tlc install' first.", err=True)
|
|
28
|
+
typer.echo("Error: tla2tools.jar not found.\nRun 'tla tools install' first.", err=True)
|
|
30
29
|
raise typer.Exit(1)
|
|
31
30
|
|
|
31
|
+
modules_dir = base_dir / config.workspace.modules_dir
|
|
32
|
+
classes_dir = base_dir / config.workspace.classes_dir
|
|
33
|
+
|
|
34
|
+
lib_dir = base_dir / "lib"
|
|
35
|
+
lib_jars = sorted(lib_dir.glob("*.jar")) if lib_dir.is_dir() else []
|
|
36
|
+
classpath = os.pathsep.join([str(jar_path)] + [str(j) for j in lib_jars])
|
|
37
|
+
|
|
32
38
|
if not modules_dir.exists():
|
|
33
39
|
typer.echo(f"Error: modules directory not found: {modules_dir}", err=True)
|
|
34
40
|
raise typer.Exit(1)
|
|
35
41
|
|
|
36
|
-
# Find all .java files in modules dir
|
|
37
42
|
java_files = list(modules_dir.rglob("*.java"))
|
|
38
43
|
if not java_files:
|
|
39
44
|
typer.echo(f"No Java source files found in {modules_dir}", err=True)
|
|
40
45
|
return
|
|
41
46
|
|
|
42
47
|
typer.echo(f"Compiling {len(java_files)} Java files from {modules_dir} ...")
|
|
43
|
-
|
|
44
|
-
# Ensure output dir exists
|
|
45
48
|
classes_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
-
|
|
47
|
-
cmd = ["javac", "-cp", str(jar_path), "-d", str(classes_dir), *[str(f) for f in java_files]]
|
|
49
|
+
cmd = ["javac", "-cp", classpath, "-d", str(classes_dir), *[str(f) for f in java_files]]
|
|
48
50
|
|
|
49
51
|
try:
|
|
50
52
|
result = subprocess.run(cmd, check=True, capture_output=not verbose, text=True)
|
|
@@ -52,14 +54,11 @@ def build(
|
|
|
52
54
|
if verbose and result.stdout:
|
|
53
55
|
typer.echo(result.stdout)
|
|
54
56
|
|
|
55
|
-
# Create META-INF/services/tlc2.overrides.ITLCOverrides
|
|
56
57
|
meta_inf = classes_dir / "META-INF" / "services"
|
|
57
58
|
meta_inf.mkdir(parents=True, exist_ok=True)
|
|
58
59
|
service_file = meta_inf / "tlc2.overrides.ITLCOverrides"
|
|
59
|
-
|
|
60
60
|
with service_file.open("w") as f:
|
|
61
61
|
f.write(f"{config.tlc.overrides_class}\n")
|
|
62
|
-
|
|
63
62
|
typer.echo(f"Created service file at {service_file}")
|
|
64
63
|
|
|
65
64
|
except subprocess.CalledProcessError as e:
|
|
@@ -8,11 +8,11 @@ from tlaplus_cli.build_tlc_module import build as build_tlc_cmd
|
|
|
8
8
|
from tlaplus_cli.check_java import check_java_version, get_java_version
|
|
9
9
|
from tlaplus_cli.config import load_config
|
|
10
10
|
from tlaplus_cli.run_tlc import tlc as run_tlc_cmd
|
|
11
|
-
from tlaplus_cli.
|
|
11
|
+
from tlaplus_cli.tools_manager import fetch_cache_app, tools_app
|
|
12
12
|
|
|
13
13
|
app = typer.Typer(
|
|
14
14
|
name="tla",
|
|
15
|
-
help="TLA+ tools: download
|
|
15
|
+
help="TLA+ tools: download TLA+ toolset distribution, compile custom modules, run model checker.",
|
|
16
16
|
no_args_is_help=True,
|
|
17
17
|
add_completion=False,
|
|
18
18
|
)
|
|
@@ -47,12 +47,15 @@ def root(
|
|
|
47
47
|
|
|
48
48
|
# --- Subcommands ---
|
|
49
49
|
|
|
50
|
-
# Attach typer subgroups
|
|
51
|
-
|
|
50
|
+
# Attach typer subgroups
|
|
51
|
+
modules_app = typer.Typer(name="modules", help="Manage TLA+ Java modules.", no_args_is_help=True)
|
|
52
|
+
modules_app.command(name="build")(build_tlc_cmd)
|
|
53
|
+
|
|
54
|
+
app.add_typer(modules_app, name="modules")
|
|
55
|
+
app.add_typer(tools_app, name="tools")
|
|
52
56
|
app.add_typer(fetch_cache_app, name="fetch-cache")
|
|
53
57
|
|
|
54
|
-
app.command(name="
|
|
55
|
-
app.command(name="build")(build_tlc_cmd)
|
|
58
|
+
app.command(name="tlc")(run_tlc_cmd)
|
|
56
59
|
|
|
57
60
|
|
|
58
61
|
@app.command(name="check-java")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Project root discovery utilities."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _is_project_root(directory: Path, modules_dir: Path | str, classes_dir: Path | str, lib_dir: Path | str) -> bool:
|
|
7
|
+
"""Return True if directory contains at least one project structure marker."""
|
|
8
|
+
return (
|
|
9
|
+
(directory / classes_dir).is_dir()
|
|
10
|
+
or (directory / modules_dir).is_dir()
|
|
11
|
+
or (directory / lib_dir).is_dir()
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def find_project_root(
|
|
16
|
+
spec_file: Path,
|
|
17
|
+
*,
|
|
18
|
+
modules_dir: Path | str,
|
|
19
|
+
classes_dir: Path | str,
|
|
20
|
+
lib_dir: Path | str = "lib",
|
|
21
|
+
) -> Path | None:
|
|
22
|
+
"""Resolve the project root relative to a .tla spec file.
|
|
23
|
+
|
|
24
|
+
Resolution strategy (in order):
|
|
25
|
+
1. Check the spec file's own directory.
|
|
26
|
+
2. Check the parent of the spec file's directory (handles spec/ and tests/ subdirs).
|
|
27
|
+
3. Return None if neither directory qualifies.
|
|
28
|
+
"""
|
|
29
|
+
spec_dir = spec_file.parent.resolve()
|
|
30
|
+
|
|
31
|
+
for candidate in (spec_dir, spec_dir.parent):
|
|
32
|
+
if _is_project_root(candidate, modules_dir, classes_dir, lib_dir):
|
|
33
|
+
return candidate
|
|
34
|
+
|
|
35
|
+
return None
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
import typer
|
|
7
8
|
|
|
8
9
|
from tlaplus_cli.check_java import check_java_version
|
|
9
|
-
from tlaplus_cli.config import cache_dir, load_config
|
|
10
|
+
from tlaplus_cli.config import cache_dir, load_config
|
|
11
|
+
from tlaplus_cli.project import find_project_root
|
|
10
12
|
from tlaplus_cli.version_manager import get_pinned_version_dir
|
|
11
13
|
|
|
12
14
|
|
|
@@ -21,7 +23,7 @@ def version_callback(value: bool) -> None:
|
|
|
21
23
|
|
|
22
24
|
if not jar_path.exists():
|
|
23
25
|
typer.echo(f"Error: tla2tools.jar not found at {jar_path}", err=True)
|
|
24
|
-
typer.echo("Run 'tla
|
|
26
|
+
typer.echo("Run 'tla tools install' first.", err=True)
|
|
25
27
|
raise typer.Exit(1)
|
|
26
28
|
|
|
27
29
|
cmd = ["java", "-cp", str(jar_path), config.tlc.java_class]
|
|
@@ -62,25 +64,42 @@ def tlc(
|
|
|
62
64
|
jar_path = pinned_jar if (pinned_jar and pinned_jar.exists()) else legacy
|
|
63
65
|
|
|
64
66
|
if not jar_path.exists():
|
|
65
|
-
typer.echo("Error: tla2tools.jar not found.", err=True)
|
|
66
|
-
typer.echo("Run 'tla tlc install' first.", err=True)
|
|
67
|
+
typer.echo("Error: tla2tools.jar not found.\nRun 'tla tools install' first.", err=True)
|
|
67
68
|
raise typer.Exit(1)
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
typer.echo(f"Error: specification not found: {spec_file}", err=True)
|
|
74
|
-
raise typer.Exit(1)
|
|
75
|
-
|
|
76
|
-
classpath = str(jar_path)
|
|
77
|
-
classes_path = ws_root / config.workspace.classes_dir
|
|
78
|
-
if classes_path.exists():
|
|
79
|
-
classpath = f"{classes_path}{os.pathsep}{classpath}"
|
|
70
|
+
# Spec resolution logic
|
|
71
|
+
spec_path = Path(spec)
|
|
72
|
+
candidates = [spec_path, spec_path.with_suffix(".tla"),
|
|
73
|
+
spec_path.parent / "spec" / (spec_path.name + ".tla")]
|
|
80
74
|
|
|
81
|
-
|
|
75
|
+
spec_file = next((c for c in candidates if c.is_file()), None)
|
|
76
|
+
if not spec_file:
|
|
77
|
+
typer.echo("Error: Could not find a TLA+ spec file. Looked in the following locations:", err=True)
|
|
78
|
+
for c in candidates:
|
|
79
|
+
typer.echo(f"- {c}", err=True)
|
|
80
|
+
raise typer.Exit(1)
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
spec_file = spec_file.absolute()
|
|
83
|
+
project_root = find_project_root(
|
|
84
|
+
spec_file, modules_dir=config.workspace.modules_dir, classes_dir=config.workspace.classes_dir
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
classpath_parts = [str(jar_path)]
|
|
88
|
+
extra_jvm_opts: list[str] = []
|
|
89
|
+
if project_root:
|
|
90
|
+
classes_path = project_root / config.workspace.classes_dir
|
|
91
|
+
if classes_path.is_dir():
|
|
92
|
+
classpath_parts.insert(0, str(classes_path))
|
|
93
|
+
lib_dir = project_root / "lib"
|
|
94
|
+
if lib_dir.is_dir():
|
|
95
|
+
classpath_parts.extend(str(j) for j in sorted(lib_dir.glob("*.jar")))
|
|
96
|
+
modules_path = project_root / config.workspace.modules_dir
|
|
97
|
+
if modules_path.is_dir():
|
|
98
|
+
extra_jvm_opts.append(f"-DTLA-Library={modules_path}")
|
|
99
|
+
|
|
100
|
+
cmd = ["java", *config.java.opts, *extra_jvm_opts, "-cp", os.pathsep.join(classpath_parts),
|
|
101
|
+
config.tlc.java_class, spec_file.name]
|
|
102
|
+
|
|
103
|
+
typer.echo(f"Running TLC on {spec_file.name} ...\nCommand: {' '.join(cmd)}")
|
|
104
|
+
result = subprocess.run(cmd, cwd=str(spec_file.parent))
|
|
86
105
|
raise typer.Exit(result.returncode)
|