fastled 1.1.3__tar.gz → 1.1.6__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.
- {fastled-1.1.3 → fastled-1.1.6}/PKG-INFO +4 -1
- {fastled-1.1.3 → fastled-1.1.6}/README.md +3 -0
- {fastled-1.1.3 → fastled-1.1.6}/pyproject.toml +1 -1
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/app.py +2 -6
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/compile_server.py +1 -1
- fastled-1.1.6/src/fastled/sketch.py +18 -0
- fastled-1.1.6/src/fastled/web_compile.py +227 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled.egg-info/PKG-INFO +4 -1
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled.egg-info/SOURCES.txt +1 -0
- fastled-1.1.3/src/fastled/web_compile.py +0 -220
- {fastled-1.1.3 → fastled-1.1.6}/.aiderignore +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.github/workflows/build_multi_docker_image.yml +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.github/workflows/lint.yml +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.github/workflows/test_macos.yml +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.github/workflows/test_ubuntu.yml +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.github/workflows/test_win.yml +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.gitignore +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.pylintrc +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.vscode/launch.json +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.vscode/settings.json +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/.vscode/tasks.json +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/LICENSE +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/MANIFEST.in +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/clean +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/docs/fastled.js +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/docs/fastled.wasm +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/docs/index.css +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/docs/index.html +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/docs/index.js +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Blink/Blink.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/Chromancer.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/detail.h +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/gary_woos_wled_port/gary_woos_wled_ledmap.h +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/gary_woos_wled_port/presets.json +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/gary_woos_wled_port/presets.min.json +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/gen.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/mapping.h +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/net.h +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/output.json +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/ripple.h +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/screenmap.json.h +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/ColorPalette/ColorPalette.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/ColorTemperature/ColorTemperature.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Cylon/Cylon.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/DemoReel100/DemoReel100.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Esp32Rmt51/Esp32Rmt51.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/EspI2SDemo/EspI2SDemo.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Fire2012/Fire2012.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Fire2012WithPalette/Fire2012WithPalette.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/FirstLight/FirstLight.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/FxEngine/FxEngine.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Noise/Noise.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/NoisePlayground/NoisePlayground.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/NoisePlusPalette/NoisePlusPalette.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/OctoWS2811/OctoWS2811.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Pacifica/Pacifica.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Pride2015/Pride2015.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/SdCard/SdCard.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/TwinkleFox/TwinkleFox.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Video/Gfx2Video/Gfx2Video.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/WasmScreenCoords/WasmScreenCoords.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/Water/Water.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/XYMatrix/XYMatrix.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/examples/wasm/wasm.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/install +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/lint +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/requirements.testing.txt +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/setup.cfg +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/setup.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/__init__.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/assets/example.txt +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/build_mode.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/check_cpp_syntax.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/cli.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/docker_manager.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/filewatcher.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/open_browser.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled/paths.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled.egg-info/dependency_links.txt +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled.egg-info/entry_points.txt +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled.egg-info/requires.txt +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/src/fastled.egg-info/top_level.txt +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/test +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/tests/test_bad_ino.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/tests/test_cli.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/tests/test_compile_server.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/tests/test_filechanger.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/tests/test_ino/bad/bad.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/tests/test_ino/wasm/wasm.ino +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/tests/test_webcompile.py +0 -0
- {fastled-1.1.3 → fastled-1.1.6}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastled
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.6
|
4
4
|
Summary: FastLED Wasm Compiler
|
5
5
|
Home-page: https://github.com/zackees/fastled-wasm
|
6
6
|
Maintainer: Zachary Vorhies
|
@@ -91,6 +91,9 @@ provide shims for most of the common api points.
|
|
91
91
|
|
92
92
|
# Revisions
|
93
93
|
|
94
|
+
* 1.1.6 - Use the fast src volume map allow quick updates to fastled when developing on the source code.
|
95
|
+
* 1.1.5 - Filter out hidden files and directories from being included in the sketch archive sent to the compiler.
|
96
|
+
* 1.1.4 - Fix regression introduced by testing out ipv4/ipv6 connections from a thread pool.
|
94
97
|
* 1.1.3 - Live editing of *.h and *.cpp files is now possible. Sketch cache will be disabled in this mode.
|
95
98
|
* 1.1.2 - `--server` will now volume map fastled src directory if it detects this. This was also implemented on the docker side.
|
96
99
|
* 1.1.1 - `--interactive` is now supported to debug the container. Volume maps and better compatibilty with ipv4/v6 by concurrent connection finding.
|
@@ -72,6 +72,9 @@ provide shims for most of the common api points.
|
|
72
72
|
|
73
73
|
# Revisions
|
74
74
|
|
75
|
+
* 1.1.6 - Use the fast src volume map allow quick updates to fastled when developing on the source code.
|
76
|
+
* 1.1.5 - Filter out hidden files and directories from being included in the sketch archive sent to the compiler.
|
77
|
+
* 1.1.4 - Fix regression introduced by testing out ipv4/ipv6 connections from a thread pool.
|
75
78
|
* 1.1.3 - Live editing of *.h and *.cpp files is now possible. Sketch cache will be disabled in this mode.
|
76
79
|
* 1.1.2 - `--server` will now volume map fastled src directory if it detects this. This was also implemented on the docker side.
|
77
80
|
* 1.1.1 - `--interactive` is now supported to debug the container. Volume maps and better compatibilty with ipv4/v6 by concurrent connection finding.
|
@@ -20,6 +20,7 @@ from fastled.compile_server import CompileServer, looks_like_fastled_repo
|
|
20
20
|
from fastled.docker_manager import DockerManager
|
21
21
|
from fastled.filewatcher import FileChangedNotifier
|
22
22
|
from fastled.open_browser import open_browser_thread
|
23
|
+
from fastled.sketch import get_sketch_files
|
23
24
|
from fastled.web_compile import web_compile
|
24
25
|
|
25
26
|
machine = platform.machine().lower()
|
@@ -210,12 +211,7 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
|
|
210
211
|
|
211
212
|
|
212
213
|
def _lots_and_lots_of_files(directory: Path) -> bool:
|
213
|
-
|
214
|
-
for root, dirs, files in os.walk(directory):
|
215
|
-
count += len(files)
|
216
|
-
if count > 100:
|
217
|
-
return True
|
218
|
-
return False
|
214
|
+
return len(get_sketch_files(directory)) > 100
|
219
215
|
|
220
216
|
|
221
217
|
def _looks_like_sketch_directory(directory: Path) -> bool:
|
@@ -151,7 +151,7 @@ class CompileServer:
|
|
151
151
|
f"Mounting FastLED source directory {self.fastled_src_dir} into container /js/fastled/src"
|
152
152
|
)
|
153
153
|
volumes = {
|
154
|
-
str(self.fastled_src_dir): {"bind": "/
|
154
|
+
str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
|
155
155
|
}
|
156
156
|
# no auto-update because the source directory is mapped in.
|
157
157
|
server_command.append("--no-auto-update") # stop git repo updates.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
|
5
|
+
def get_sketch_files(directory: Path) -> list[Path]:
|
6
|
+
files: list[Path] = []
|
7
|
+
for root, dirs, filenames in os.walk(directory):
|
8
|
+
# ignore hidden directories
|
9
|
+
dirs[:] = [d for d in dirs if not d.startswith(".")]
|
10
|
+
# ignore fastled_js directory
|
11
|
+
dirs[:] = [d for d in dirs if "fastled_js" not in d]
|
12
|
+
# ignore hidden files
|
13
|
+
filenames = [f for f in filenames if not f.startswith(".")]
|
14
|
+
for filename in filenames:
|
15
|
+
if "platformio.ini" in filename:
|
16
|
+
continue
|
17
|
+
files.append(Path(root) / filename)
|
18
|
+
return files
|
@@ -0,0 +1,227 @@
|
|
1
|
+
import io
|
2
|
+
import shutil
|
3
|
+
import tempfile
|
4
|
+
import zipfile
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
6
|
+
from dataclasses import dataclass
|
7
|
+
from pathlib import Path
|
8
|
+
|
9
|
+
import httpx
|
10
|
+
|
11
|
+
from fastled.build_mode import BuildMode
|
12
|
+
from fastled.compile_server import SERVER_PORT
|
13
|
+
from fastled.sketch import get_sketch_files
|
14
|
+
|
15
|
+
DEFAULT_HOST = "https://fastled.onrender.com"
|
16
|
+
ENDPOINT_COMPILED_WASM = "compile/wasm"
|
17
|
+
_TIMEOUT = 60 * 4 # 2 mins timeout
|
18
|
+
_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
|
19
|
+
|
20
|
+
_THREAD_POOL = ThreadPoolExecutor(max_workers=8)
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class TestConnectionResult:
|
25
|
+
host: str
|
26
|
+
success: bool
|
27
|
+
ipv4: bool
|
28
|
+
|
29
|
+
|
30
|
+
@dataclass
|
31
|
+
class WebCompileResult:
|
32
|
+
success: bool
|
33
|
+
stdout: str
|
34
|
+
hash_value: str | None
|
35
|
+
zip_bytes: bytes
|
36
|
+
|
37
|
+
def __bool__(self) -> bool:
|
38
|
+
return self.success
|
39
|
+
|
40
|
+
|
41
|
+
def _sanitize_host(host: str) -> str:
|
42
|
+
if host.startswith("http"):
|
43
|
+
return host
|
44
|
+
is_local_host = "localhost" in host or "127.0.0.1" in host or "0.0.0.0" in host
|
45
|
+
use_https = not is_local_host
|
46
|
+
if use_https:
|
47
|
+
return host if host.startswith("https://") else f"https://{host}"
|
48
|
+
return host if host.startswith("http://") else f"http://{host}"
|
49
|
+
|
50
|
+
|
51
|
+
_CONNECTION_ERROR_MAP: dict[str, TestConnectionResult] = {}
|
52
|
+
|
53
|
+
|
54
|
+
def _test_connection(host: str, use_ipv4: bool) -> TestConnectionResult:
|
55
|
+
key = f"{host}-{use_ipv4}"
|
56
|
+
maybe_result: TestConnectionResult | None = _CONNECTION_ERROR_MAP.get(key)
|
57
|
+
if maybe_result is not None:
|
58
|
+
return maybe_result
|
59
|
+
transport = httpx.HTTPTransport(local_address="0.0.0.0") if use_ipv4 else None
|
60
|
+
try:
|
61
|
+
with httpx.Client(
|
62
|
+
timeout=_TIMEOUT,
|
63
|
+
transport=transport,
|
64
|
+
) as test_client:
|
65
|
+
test_response = test_client.get(
|
66
|
+
f"{host}/healthz", timeout=3, follow_redirects=True
|
67
|
+
)
|
68
|
+
result = TestConnectionResult(
|
69
|
+
host, test_response.status_code == 200, use_ipv4
|
70
|
+
)
|
71
|
+
_CONNECTION_ERROR_MAP[key] = result
|
72
|
+
except Exception:
|
73
|
+
result = TestConnectionResult(host, False, use_ipv4)
|
74
|
+
_CONNECTION_ERROR_MAP[key] = result
|
75
|
+
return result
|
76
|
+
|
77
|
+
|
78
|
+
def zip_files(directory: Path) -> bytes | Exception:
|
79
|
+
print("Zipping files...")
|
80
|
+
try:
|
81
|
+
files = get_sketch_files(directory)
|
82
|
+
if not files:
|
83
|
+
raise FileNotFoundError(f"No files found in {directory}")
|
84
|
+
for f in files:
|
85
|
+
print(f"Adding file: {f}")
|
86
|
+
# Create in-memory zip file
|
87
|
+
zip_buffer = io.BytesIO()
|
88
|
+
with zipfile.ZipFile(
|
89
|
+
zip_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
|
90
|
+
) as zip_file:
|
91
|
+
for file_path in files:
|
92
|
+
relative_path = file_path.relative_to(directory)
|
93
|
+
zip_file.write(file_path, str(Path("wasm") / relative_path))
|
94
|
+
return zip_buffer.getvalue()
|
95
|
+
except Exception as e:
|
96
|
+
return e
|
97
|
+
|
98
|
+
|
99
|
+
def web_compile(
|
100
|
+
directory: Path,
|
101
|
+
host: str | None = None,
|
102
|
+
auth_token: str | None = None,
|
103
|
+
build_mode: BuildMode | None = None,
|
104
|
+
profile: bool = False,
|
105
|
+
) -> WebCompileResult:
|
106
|
+
host = _sanitize_host(host or DEFAULT_HOST)
|
107
|
+
print("Compiling on", host)
|
108
|
+
auth_token = auth_token or _AUTH_TOKEN
|
109
|
+
|
110
|
+
if not directory.exists():
|
111
|
+
raise FileNotFoundError(f"Directory not found: {directory}")
|
112
|
+
|
113
|
+
zip_bytes = zip_files(directory)
|
114
|
+
if isinstance(zip_bytes, Exception):
|
115
|
+
return WebCompileResult(
|
116
|
+
success=False, stdout=str(zip_bytes), hash_value=None, zip_bytes=b""
|
117
|
+
)
|
118
|
+
archive_size = len(zip_bytes)
|
119
|
+
print(f"Web compiling on {host}...")
|
120
|
+
try:
|
121
|
+
|
122
|
+
files = {"file": ("wasm.zip", zip_bytes, "application/x-zip-compressed")}
|
123
|
+
urls = [host]
|
124
|
+
domain = host.split("://")[-1]
|
125
|
+
if ":" not in domain:
|
126
|
+
urls.append(f"{host}:{SERVER_PORT}")
|
127
|
+
test_connection_result: TestConnectionResult | None = None
|
128
|
+
|
129
|
+
futures: list = []
|
130
|
+
ip_versions = [True, False] if "localhost" not in host else [True]
|
131
|
+
for ipv4 in ip_versions:
|
132
|
+
for url in urls:
|
133
|
+
f = _THREAD_POOL.submit(_test_connection, url, ipv4)
|
134
|
+
futures.append(f)
|
135
|
+
|
136
|
+
succeeded = False
|
137
|
+
for future in as_completed(futures):
|
138
|
+
result: TestConnectionResult = future.result()
|
139
|
+
|
140
|
+
if result.success:
|
141
|
+
print(f"Connection successful to {result.host}")
|
142
|
+
succeeded = True
|
143
|
+
# host = test_url
|
144
|
+
test_connection_result = result
|
145
|
+
break
|
146
|
+
else:
|
147
|
+
print(f"Ignoring {result.host} due to connection failure")
|
148
|
+
|
149
|
+
if not succeeded:
|
150
|
+
print("Connection failed to all endpoints")
|
151
|
+
return WebCompileResult(
|
152
|
+
success=False,
|
153
|
+
stdout="Connection failed",
|
154
|
+
hash_value=None,
|
155
|
+
zip_bytes=b"",
|
156
|
+
)
|
157
|
+
assert test_connection_result is not None
|
158
|
+
ipv4_stmt = "IPv4" if test_connection_result.ipv4 else "IPv6"
|
159
|
+
transport = (
|
160
|
+
httpx.HTTPTransport(local_address="0.0.0.0")
|
161
|
+
if test_connection_result.ipv4
|
162
|
+
else None
|
163
|
+
)
|
164
|
+
with httpx.Client(
|
165
|
+
transport=transport,
|
166
|
+
timeout=_TIMEOUT,
|
167
|
+
) as client:
|
168
|
+
headers = {
|
169
|
+
"accept": "application/json",
|
170
|
+
"authorization": auth_token,
|
171
|
+
"build": (
|
172
|
+
build_mode.value.lower()
|
173
|
+
if build_mode
|
174
|
+
else BuildMode.QUICK.value.lower()
|
175
|
+
),
|
176
|
+
"profile": "true" if profile else "false",
|
177
|
+
}
|
178
|
+
|
179
|
+
url = f"{test_connection_result.host}/{ENDPOINT_COMPILED_WASM}"
|
180
|
+
print(f"Compiling on {url} via {ipv4_stmt}. Zip size: {archive_size} bytes")
|
181
|
+
response = client.post(
|
182
|
+
url,
|
183
|
+
follow_redirects=True,
|
184
|
+
files=files,
|
185
|
+
headers=headers,
|
186
|
+
timeout=_TIMEOUT,
|
187
|
+
)
|
188
|
+
|
189
|
+
if response.status_code != 200:
|
190
|
+
json_response = response.json()
|
191
|
+
detail = json_response.get("detail", "Could not compile")
|
192
|
+
return WebCompileResult(
|
193
|
+
success=False, stdout=detail, hash_value=None, zip_bytes=b""
|
194
|
+
)
|
195
|
+
|
196
|
+
print(f"Response status code: {response}")
|
197
|
+
# Create a temporary directory to extract the zip
|
198
|
+
with tempfile.TemporaryDirectory() as extract_dir:
|
199
|
+
extract_path = Path(extract_dir)
|
200
|
+
|
201
|
+
# Write the response content to a temporary zip file
|
202
|
+
temp_zip = extract_path / "response.zip"
|
203
|
+
temp_zip.write_bytes(response.content)
|
204
|
+
|
205
|
+
# Extract the zip
|
206
|
+
shutil.unpack_archive(temp_zip, extract_path, "zip")
|
207
|
+
|
208
|
+
# Read stdout from out.txt if it exists
|
209
|
+
stdout_file = extract_path / "out.txt"
|
210
|
+
hash_file = extract_path / "hash.txt"
|
211
|
+
stdout = stdout_file.read_text() if stdout_file.exists() else ""
|
212
|
+
hash_value = hash_file.read_text() if hash_file.exists() else None
|
213
|
+
|
214
|
+
return WebCompileResult(
|
215
|
+
success=True,
|
216
|
+
stdout=stdout,
|
217
|
+
hash_value=hash_value,
|
218
|
+
zip_bytes=response.content,
|
219
|
+
)
|
220
|
+
except KeyboardInterrupt:
|
221
|
+
print("Keyboard interrupt")
|
222
|
+
raise
|
223
|
+
except httpx.HTTPError as e:
|
224
|
+
print(f"Error: {e}")
|
225
|
+
return WebCompileResult(
|
226
|
+
success=False, stdout=str(e), hash_value=None, zip_bytes=b""
|
227
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastled
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.6
|
4
4
|
Summary: FastLED Wasm Compiler
|
5
5
|
Home-page: https://github.com/zackees/fastled-wasm
|
6
6
|
Maintainer: Zachary Vorhies
|
@@ -91,6 +91,9 @@ provide shims for most of the common api points.
|
|
91
91
|
|
92
92
|
# Revisions
|
93
93
|
|
94
|
+
* 1.1.6 - Use the fast src volume map allow quick updates to fastled when developing on the source code.
|
95
|
+
* 1.1.5 - Filter out hidden files and directories from being included in the sketch archive sent to the compiler.
|
96
|
+
* 1.1.4 - Fix regression introduced by testing out ipv4/ipv6 connections from a thread pool.
|
94
97
|
* 1.1.3 - Live editing of *.h and *.cpp files is now possible. Sketch cache will be disabled in this mode.
|
95
98
|
* 1.1.2 - `--server` will now volume map fastled src directory if it detects this. This was also implemented on the docker side.
|
96
99
|
* 1.1.1 - `--interactive` is now supported to debug the container. Volume maps and better compatibilty with ipv4/v6 by concurrent connection finding.
|
@@ -1,220 +0,0 @@
|
|
1
|
-
import shutil
|
2
|
-
import tempfile
|
3
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
4
|
-
from dataclasses import dataclass
|
5
|
-
from pathlib import Path
|
6
|
-
|
7
|
-
import httpx
|
8
|
-
|
9
|
-
from fastled.build_mode import BuildMode
|
10
|
-
from fastled.compile_server import SERVER_PORT
|
11
|
-
|
12
|
-
DEFAULT_HOST = "https://fastled.onrender.com"
|
13
|
-
ENDPOINT_COMPILED_WASM = "compile/wasm"
|
14
|
-
_TIMEOUT = 60 * 4 # 2 mins timeout
|
15
|
-
_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
|
16
|
-
|
17
|
-
|
18
|
-
@dataclass
|
19
|
-
class TestConnectionResult:
|
20
|
-
host: str
|
21
|
-
success: bool
|
22
|
-
ipv4: bool
|
23
|
-
|
24
|
-
|
25
|
-
@dataclass
|
26
|
-
class WebCompileResult:
|
27
|
-
success: bool
|
28
|
-
stdout: str
|
29
|
-
hash_value: str | None
|
30
|
-
zip_bytes: bytes
|
31
|
-
|
32
|
-
def __bool__(self) -> bool:
|
33
|
-
return self.success
|
34
|
-
|
35
|
-
|
36
|
-
def _sanitize_host(host: str) -> str:
|
37
|
-
if host.startswith("http"):
|
38
|
-
return host
|
39
|
-
is_local_host = "localhost" in host or "127.0.0.1" in host or "0.0.0.0" in host
|
40
|
-
use_https = not is_local_host
|
41
|
-
if use_https:
|
42
|
-
return host if host.startswith("https://") else f"https://{host}"
|
43
|
-
return host if host.startswith("http://") else f"http://{host}"
|
44
|
-
|
45
|
-
|
46
|
-
_CONNECTION_ERROR_MAP: dict[str, TestConnectionResult] = {}
|
47
|
-
|
48
|
-
|
49
|
-
def _test_connection(host: str, use_ipv4: bool) -> TestConnectionResult:
|
50
|
-
key = f"{host}-{use_ipv4}"
|
51
|
-
maybe_result: TestConnectionResult | None = _CONNECTION_ERROR_MAP.get(key)
|
52
|
-
if maybe_result is not None:
|
53
|
-
return maybe_result
|
54
|
-
transport = httpx.HTTPTransport(local_address="0.0.0.0") if use_ipv4 else None
|
55
|
-
try:
|
56
|
-
with httpx.Client(
|
57
|
-
timeout=_TIMEOUT,
|
58
|
-
transport=transport,
|
59
|
-
) as test_client:
|
60
|
-
test_response = test_client.get(
|
61
|
-
f"{host}/healthz", timeout=_TIMEOUT, follow_redirects=True
|
62
|
-
)
|
63
|
-
result = TestConnectionResult(
|
64
|
-
host, test_response.status_code == 200, use_ipv4
|
65
|
-
)
|
66
|
-
_CONNECTION_ERROR_MAP[key] = result
|
67
|
-
except Exception:
|
68
|
-
result = TestConnectionResult(host, False, use_ipv4)
|
69
|
-
_CONNECTION_ERROR_MAP[key] = result
|
70
|
-
return result
|
71
|
-
|
72
|
-
|
73
|
-
def web_compile(
|
74
|
-
directory: Path,
|
75
|
-
host: str | None = None,
|
76
|
-
auth_token: str | None = None,
|
77
|
-
build_mode: BuildMode | None = None,
|
78
|
-
profile: bool = False,
|
79
|
-
) -> WebCompileResult:
|
80
|
-
host = _sanitize_host(host or DEFAULT_HOST)
|
81
|
-
print("Compiling on", host)
|
82
|
-
auth_token = auth_token or _AUTH_TOKEN
|
83
|
-
# zip up the files
|
84
|
-
print("Zipping files...")
|
85
|
-
|
86
|
-
# Create a temporary zip file
|
87
|
-
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_zip:
|
88
|
-
# Create temporary directory for organizing files
|
89
|
-
with tempfile.TemporaryDirectory() as temp_dir:
|
90
|
-
# Create wasm subdirectory
|
91
|
-
wasm_dir = Path(temp_dir) / "wasm"
|
92
|
-
|
93
|
-
# Copy all files from source to wasm subdirectory, excluding fastled_js
|
94
|
-
def ignore_fastled_js(dir: str, files: list[str]) -> list[str]:
|
95
|
-
if "fastled_js" in dir:
|
96
|
-
return files
|
97
|
-
if dir.startswith("."):
|
98
|
-
return files
|
99
|
-
return []
|
100
|
-
|
101
|
-
shutil.copytree(directory, wasm_dir, ignore=ignore_fastled_js)
|
102
|
-
# Create zip archive from the temp directory
|
103
|
-
shutil.make_archive(tmp_zip.name[:-4], "zip", temp_dir)
|
104
|
-
|
105
|
-
print(f"Web compiling on {host}...")
|
106
|
-
|
107
|
-
try:
|
108
|
-
with open(tmp_zip.name, "rb") as zip_file:
|
109
|
-
files = {"file": ("wasm.zip", zip_file, "application/x-zip-compressed")}
|
110
|
-
urls = [host]
|
111
|
-
domain = host.split("://")[-1]
|
112
|
-
if ":" not in domain:
|
113
|
-
urls.append(f"{host}:{SERVER_PORT}")
|
114
|
-
test_connection_result: TestConnectionResult | None = None
|
115
|
-
|
116
|
-
with ThreadPoolExecutor(max_workers=len(urls)) as executor:
|
117
|
-
futures: list = []
|
118
|
-
ip_versions = [True, False] if "localhost" not in host else [True]
|
119
|
-
for ipv4 in ip_versions:
|
120
|
-
for url in urls:
|
121
|
-
f = executor.submit(_test_connection, url, ipv4)
|
122
|
-
futures.append(f)
|
123
|
-
|
124
|
-
succeeded = False
|
125
|
-
for future in as_completed(futures):
|
126
|
-
result: TestConnectionResult = future.result()
|
127
|
-
|
128
|
-
if result.success:
|
129
|
-
print(f"Connection successful to {result.host}")
|
130
|
-
succeeded = True
|
131
|
-
# host = test_url
|
132
|
-
test_connection_result = result
|
133
|
-
break
|
134
|
-
else:
|
135
|
-
print(f"Ignoring {result.host} due to connection failure")
|
136
|
-
|
137
|
-
if not succeeded:
|
138
|
-
print("Connection failed to all endpoints")
|
139
|
-
return WebCompileResult(
|
140
|
-
success=False,
|
141
|
-
stdout="Connection failed",
|
142
|
-
hash_value=None,
|
143
|
-
zip_bytes=b"",
|
144
|
-
)
|
145
|
-
assert test_connection_result is not None
|
146
|
-
ipv4_stmt = "IPv4" if test_connection_result.ipv4 else "IPv6"
|
147
|
-
transport = (
|
148
|
-
httpx.HTTPTransport(local_address="0.0.0.0")
|
149
|
-
if test_connection_result.ipv4
|
150
|
-
else None
|
151
|
-
)
|
152
|
-
with httpx.Client(
|
153
|
-
transport=transport,
|
154
|
-
timeout=_TIMEOUT,
|
155
|
-
) as client:
|
156
|
-
headers = {
|
157
|
-
"accept": "application/json",
|
158
|
-
"authorization": auth_token,
|
159
|
-
"build": (
|
160
|
-
build_mode.value.lower()
|
161
|
-
if build_mode
|
162
|
-
else BuildMode.QUICK.value.lower()
|
163
|
-
),
|
164
|
-
"profile": "true" if profile else "false",
|
165
|
-
}
|
166
|
-
|
167
|
-
url = f"{test_connection_result.host}/{ENDPOINT_COMPILED_WASM}"
|
168
|
-
print(f"Compiling on {url} via {ipv4_stmt}")
|
169
|
-
response = client.post(
|
170
|
-
url,
|
171
|
-
follow_redirects=True,
|
172
|
-
files=files,
|
173
|
-
headers=headers,
|
174
|
-
timeout=_TIMEOUT,
|
175
|
-
)
|
176
|
-
|
177
|
-
if response.status_code != 200:
|
178
|
-
json_response = response.json()
|
179
|
-
detail = json_response.get("detail", "Could not compile")
|
180
|
-
return WebCompileResult(
|
181
|
-
success=False, stdout=detail, hash_value=None, zip_bytes=b""
|
182
|
-
)
|
183
|
-
|
184
|
-
print(f"Response status code: {response}")
|
185
|
-
# Create a temporary directory to extract the zip
|
186
|
-
with tempfile.TemporaryDirectory() as extract_dir:
|
187
|
-
extract_path = Path(extract_dir)
|
188
|
-
|
189
|
-
# Write the response content to a temporary zip file
|
190
|
-
temp_zip = extract_path / "response.zip"
|
191
|
-
temp_zip.write_bytes(response.content)
|
192
|
-
|
193
|
-
# Extract the zip
|
194
|
-
shutil.unpack_archive(temp_zip, extract_path, "zip")
|
195
|
-
|
196
|
-
# Read stdout from out.txt if it exists
|
197
|
-
stdout_file = extract_path / "out.txt"
|
198
|
-
hash_file = extract_path / "hash.txt"
|
199
|
-
stdout = stdout_file.read_text() if stdout_file.exists() else ""
|
200
|
-
hash_value = hash_file.read_text() if hash_file.exists() else None
|
201
|
-
|
202
|
-
return WebCompileResult(
|
203
|
-
success=True,
|
204
|
-
stdout=stdout,
|
205
|
-
hash_value=hash_value,
|
206
|
-
zip_bytes=response.content,
|
207
|
-
)
|
208
|
-
except KeyboardInterrupt:
|
209
|
-
print("Keyboard interrupt")
|
210
|
-
raise
|
211
|
-
except httpx.HTTPError as e:
|
212
|
-
print(f"Error: {e}")
|
213
|
-
return WebCompileResult(
|
214
|
-
success=False, stdout=str(e), hash_value=None, zip_bytes=b""
|
215
|
-
)
|
216
|
-
finally:
|
217
|
-
try:
|
218
|
-
Path(tmp_zip.name).unlink()
|
219
|
-
except PermissionError:
|
220
|
-
print("Warning: Could not delete temporary zip file")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{fastled-1.1.3 → fastled-1.1.6}/examples/Chromancer/gary_woos_wled_port/gary_woos_wled_ledmap.h
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|