Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/actions/build-sdk-posix/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ runs:
cp -r release/TactilitySDK $TACTILITY_PLATFORM_PATH/TactilitySDK
mkdir -p test_sdk/sdk/$(cat version.txt)/tools
cp Buildscripts/TactilitySDK/Tools/*.py test_sdk/sdk/$(cat version.txt)/tools/
python3 -c "import sys; sys.path.insert(0, 'Buildscripts/CDN'); from new_project import create_new_project_zip; create_new_project_zip('test_sdk/sdk/$(cat version.txt)')"
cp -r Buildscripts/TactilitySDK/NewProject test_sdk/sdk/$(cat version.txt)/tools/NewProject
- name: 'Test Integration'
shell: bash
env:
Expand All @@ -54,12 +54,12 @@ runs:
TACTILITY_ARCH: ${{ steps.arch.outputs.value }}
run: |
SDK_VERSION=$(cat version.txt)
# The answers are: generate, SDK version, project type, package id, app id and app name. An empty line takes the default.
# The answers are: SDK version, project type, package id, app id and app name. An empty line takes the default.
# A module has no app id or app name, so the extra lines are ignored.
for type in "" "terminal" "module"; do
project_path="$RUNNER_TEMP/new-project-${type:-gui}"
mkdir -p "$project_path"
(cd "$project_path" && printf 'y\n%s\n%s\n\n\n\n' "$SDK_VERSION" "$type" | TACTILITY_SDK_PATH="$GITHUB_WORKSPACE/test_sdk" python3 "$GITHUB_WORKSPACE/tt.py" app build -a $TACTILITY_ARCH)
(cd "$project_path" && printf '%s\n%s\n\n\n\n' "$SDK_VERSION" "$type" | TACTILITY_SDK_PATH="$GITHUB_WORKSPACE/test_sdk" python3 "$GITHUB_WORKSPACE/tt.py" app create && TACTILITY_SDK_PATH="$GITHUB_WORKSPACE/test_sdk" python3 "$GITHUB_WORKSPACE/tt.py" app build -a $TACTILITY_ARCH)
# The project must have produced a binary for the manifest's entry
test -f "$project_path/build/package-intermediate/bin/$TACTILITY_ARCH/main.so"
done
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/build-sdk/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ runs:
cp -r release/TactilitySDK $TACTILITY_PLATFORM_PATH/TactilitySDK
mkdir -p test_sdk/sdk/$(cat version.txt)/tools
cp Buildscripts/TactilitySDK/Tools/*.py test_sdk/sdk/$(cat version.txt)/tools/
python -c "import sys; sys.path.insert(0, 'Buildscripts/CDN'); from new_project import create_new_project_zip; create_new_project_zip('test_sdk/sdk/$(cat version.txt)')"
cp -r Buildscripts/TactilitySDK/NewProject test_sdk/sdk/$(cat version.txt)/tools/NewProject
- name: 'Test Integration'
uses: espressif/esp-idf-ci-action@v1
with:
Expand Down
12 changes: 7 additions & 5 deletions Buildscripts/CDN/generate-sdk-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
import shutil
import zipfile

from new_project import create_new_project_zip

VERBOSE = False
TOOLS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "TactilitySDK", "Tools")
NEW_PROJECT_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "TactilitySDK", "NewProject")

@dataclass
class SdkIndex:
Expand All @@ -20,7 +19,6 @@ class SdkIndex:
gitCommit: str
platforms: dict
tools: dict
Comment thread
KenVanHoeylandt marked this conversation as resolved.
Resolved
newProject: dict
simulators: dict

shell_color_red = "\033[91m"
Expand Down Expand Up @@ -49,14 +47,19 @@ def get_git_commit_hash():
return subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('ascii').strip()

def create_tools_bundle(out_path: str):
"""Bundles the tools that tt.py runs for this SDK version"""
"""Bundles the tools that tt.py runs for this SDK version, with the project templates of "tt app create" in NewProject/"""
if VERBOSE:
print(f"Archiving {TOOLS_PATH} to {out_path}/tools.zip")
archive_path = os.path.join(out_path, "tools.zip")
with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as archive:
for file_name in sorted(os.listdir(TOOLS_PATH)):
if file_name.endswith(".py"):
archive.write(os.path.join(TOOLS_PATH, file_name), file_name)
for directory, directory_names, file_names in os.walk(NEW_PROJECT_PATH):
directory_names.sort()
for file_name in sorted(file_names):
file_path = os.path.join(directory, file_name)
archive.write(file_path, os.path.join("NewProject", os.path.relpath(file_path, NEW_PROJECT_PATH)))
with open(archive_path, "rb") as archive:
sha256 = hashlib.sha256(archive.read()).hexdigest()
return {"file": "tools.zip", "sha256": sha256}
Expand Down Expand Up @@ -84,7 +87,6 @@ def main(in_path: str, out_path: str, version: str):
gitCommit=get_git_commit_hash(),
platforms={},
tools=create_tools_bundle(out_path),
newProject=create_new_project_zip(out_path),
simulators={}
)
for artifact_directory in artifact_directories:
Expand Down
19 changes: 0 additions & 19 deletions Buildscripts/CDN/new_project.py

This file was deleted.

64 changes: 64 additions & 0 deletions Buildscripts/TactilitySDK/Tools/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

import _device_host

ID_PATTERN = re.compile(r"^[0-9A-Za-z.]{5,32}$")
NAME_PATTERN = re.compile(r"^[0-9A-Za-z -]{2,32}$")
PROJECT_VARIANTS = ("gui", "terminal", "module")
ttbuild_version = "8.0.0"
verbose = False
# Compile the ESP-IDF and SDK components through ccache (see TactilitySDK.esp32.cmake)
Expand All @@ -34,6 +37,9 @@ def print_help():
print("")
print("Actions:")
print("")
print(" create Create a new app or module project in the current directory.")
print(" Asks for the project type, package id, app id and app name.")
print("")
print(" build Build the app. Optionally specify a platform.")
print(" Supported platforms are lower case. Example: esp32s3")
print(" Supported platforms are read from manifest.properties (target.platforms=all for every installed SDK platform)")
Expand Down Expand Up @@ -72,6 +78,7 @@ def print_help():
print(" --verbose Show extra console output")
print("")
print("Examples:")
print(f" {command} create")
print(f" {command} build")
print(f" {command} build --path Apps/Snake --architecture esp32s3 --verbose")
print(f" {command} bir --path Apps/Snake --host 192.168.1.50 --architecture esp32s3")
Expand Down Expand Up @@ -791,6 +798,58 @@ def uninstall_action(manifest, arguments):
device.uninstall(host, device.DEFAULT_PORT, app_id)
print_status_success("Uninstalled")

#region Create

def ask(question, default, is_valid, description):
"""Asks until the answer is valid. An empty answer or the end of the input gives the default."""
while True:
try:
answer = input(f"{question} [{default}]: ").strip()
except EOFError:
answer = ""
if not answer:
return default
if is_valid(answer):
return answer
print_error(description)

def create_action():
if tt_sdk_version is None:
exit_with_error("TACTILITY_SDK_VERSION is not set: run this tool as 'tt app create'")
if os.path.exists("manifest.properties") or os.path.exists("main"):
exit_with_error("This folder already has a manifest.properties or main/")
templates_path = os.path.join(tool_directory, "NewProject")
if not os.path.isdir(templates_path):
exit_with_error(f"SDK {tt_sdk_version} has no new project templates")
variant = ask("Project type (gui, terminal or module)", "gui", lambda answer: answer in PROJECT_VARIANTS, "Use gui, terminal or module")
package_id = ask("Package id", "example.helloworld", ID_PATTERN.match, "Use 5 to 32 letters, digits and dots")
app_id = app_name = ""
if variant != "module":
app_id = ask("App id", "helloworld", ID_PATTERN.match, "Use 5 to 32 letters, digits and dots")
app_name = ask("App name", "Hello World", NAME_PATTERN.match, "Use 2 to 32 letters, digits, spaces and dashes")
placeholders = (("@SDK_VERSION@", tt_sdk_version), ("@PACKAGE_ID@", package_id), ("@APP_ID@", app_id), ("@APP_NAME@", app_name))
variant_path = os.path.join(templates_path, variant)
if not os.path.isfile(os.path.join(variant_path, "manifest.properties")):
exit_with_error(f"SDK {tt_sdk_version} has no {variant} project template")
for directory, directory_names, file_names in os.walk(variant_path):
directory_names.sort()
for file_name in sorted(file_names):
source = os.path.join(directory, file_name)
relative_path = os.path.relpath(source, variant_path)
with open(source, "rb") as file:
content = file.read()
if relative_path == "manifest.properties":
text = content.decode("utf-8")
for placeholder, value in placeholders:
text = text.replace(placeholder, value)
content = text.encode("utf-8")
os.makedirs(os.path.dirname(relative_path) or ".", exist_ok=True)
with open(relative_path, "wb") as file:
file.write(content)
print(f"Created {relative_path}")

#endregion Create

#region Main

if __name__ == "__main__":
Expand Down Expand Up @@ -818,6 +877,11 @@ def uninstall_action(manifest, arguments):
verbose = parsed_command.arguments.has("verbose")
use_ccache = not parsed_command.arguments.has("no-ccache")

# Creating a project and installing an app file don't need an app project
if parsed_command.action == "create":
create_action()
sys.exit()

# Installing an app file doesn't need an app project
if parsed_command.arguments.has("app-file"):
if not install_action(None, parsed_command.arguments):
Expand Down
26 changes: 0 additions & 26 deletions Buildscripts/TactilitySDK/Tools/host.py

This file was deleted.

9 changes: 3 additions & 6 deletions Buildscripts/release-tools-current.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
#!/usr/bin/env python3
"""Exports the tools that tt runs to release/sdk/<version>/tools/ and the new project to release/sdk/<version>/new-project.zip, for tt with TACTILITY_SDK_PATH=release."""
"""Exports the tools that tt runs, with the new project templates in NewProject/, to release/sdk/<version>/tools/, for tt with TACTILITY_SDK_PATH=release."""

import os
import shutil
import sys

ROOT_PATH = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
sys.path.insert(0, os.path.join(ROOT_PATH, "Buildscripts", "CDN"))

from new_project import create_new_project_zip
TOOLS_PATH = os.path.join(ROOT_PATH, "Buildscripts", "TactilitySDK", "Tools")
NEW_PROJECT_PATH = os.path.join(ROOT_PATH, "Buildscripts", "TactilitySDK", "NewProject")


if __name__ == "__main__":
Expand All @@ -21,6 +19,5 @@
for file_name in sorted(os.listdir(TOOLS_PATH)):
if file_name.endswith(".py"):
shutil.copy2(os.path.join(TOOLS_PATH, file_name), os.path.join(output_path, file_name))
# Next to the tools, where tt looks for the new project of a local SDK
create_new_project_zip(os.path.dirname(output_path))
shutil.copytree(NEW_PROJECT_PATH, os.path.join(output_path, "NewProject"))
Comment thread
KenVanHoeylandt marked this conversation as resolved.
Resolved
print(f"Exported the tools to {output_path}")
5 changes: 3 additions & 2 deletions Buildscripts/test-new-project.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ rm -rf "$project"
mkdir -p "$project"
cd "$project"

# The answers are: generate, SDK version, project type and package id. An empty line takes the default.
printf 'y\n%s\nmodule\n\n' "$version" | TACTILITY_SDK_PATH="$root/test_sdk" python "$root/tt.py" app build -a "$platform"
# The answers are: SDK version, project type and package id. An empty line takes the default.
printf '%s\nmodule\n\n' "$version" | TACTILITY_SDK_PATH="$root/test_sdk" python "$root/tt.py" app create
TACTILITY_SDK_PATH="$root/test_sdk" python "$root/tt.py" app build -a "$platform"

# The entry point of a module is renamed by the ELF build. A binary without one is tiny.
test "$(stat -c %s "build/package-intermediate/bin/$platform/main.elf")" -gt 600
4 changes: 4 additions & 0 deletions Modules/development-module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ The C API is in `<development/server.h>`.
- `0x21` uninstall `[package id]`: answered with `0x15` ok, also when the package isn't installed, or `0x16` fail `[message]`
- `0x22` run `[app id]`: starts the app, or restarts it when it's in the foreground. Answered with `0x15` ok, or `0x16` fail `[message]`

7. In `info` mode, no client certificate is needed and the client isn't prompted for. The server answers with one JSON line and closes the connection:
`{"status":"accepted","info":{"vendor":"LilyGO","name":"T-Deck","id":"lilygo-tdeck","version":"0.8.0"}}`
The vendor is empty for devices without one.

`Buildscripts/TactilitySDK/Tools/shell.py` is a client for this server. App developers run it as `tt shell`, and copy files with `tt push` and `tt pull`.

License: [Apache v2.0](LICENSE-Apache-2.0.md)
6 changes: 6 additions & 0 deletions Modules/development-module/private/development/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ DevelopmentHelloError development_protocol_parse_hello(const char* json, size_t
*/
size_t development_protocol_format_status(const char* status, const char* message, char* out_line, size_t out_size);

/**
* Writes the JSON line that answers an "info" hello, including its newline.
* @return the line length, or 0 when @a out_size is too small
*/
size_t development_protocol_format_info(const char* vendor, const char* name, const char* id, const char* version, char* out_line, size_t out_size);

struct DevelopmentFrame {
DevelopmentFrameType type;
const uint8_t* payload;
Expand Down
3 changes: 3 additions & 0 deletions Modules/development-module/private/development/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ bool development_session_write(DevelopmentSession* session, const void* data, si

bool development_session_send_status(DevelopmentSession* session, const char* status, const char* message);

/** Sends the "accepted" line of an "info" hello, with the device information */
bool development_session_send_info(DevelopmentSession* session, const char* vendor, const char* name, const char* id, const char* version);

bool development_session_send_frame(DevelopmentSession* session, DevelopmentFrameType type, const void* payload, uint16_t length);

/** Hands the session that the listener accepted in @a slot to its session app. @return NULL when there is none */
Expand Down
25 changes: 24 additions & 1 deletion Modules/development-module/source/protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ DevelopmentHelloError development_protocol_parse_hello(const char* json, size_t
*out_message = "unsupported protocol version";
return DevelopmentHelloError::UnsupportedProtocol;
}
if (strcmp(hello.mode, "shell") != 0 && strcmp(hello.mode, "sync") != 0 && strcmp(hello.mode, "app") != 0) {
if (strcmp(hello.mode, "shell") != 0 && strcmp(hello.mode, "sync") != 0 && strcmp(hello.mode, "app") != 0 && strcmp(hello.mode, "info") != 0) {
*out_message = "unsupported mode";
return DevelopmentHelloError::UnsupportedMode;
}
Expand Down Expand Up @@ -99,6 +99,29 @@ size_t development_protocol_format_status(const char* status, const char* messag
return length + 1;
}

size_t development_protocol_format_info(const char* vendor, const char* name, const char* id, const char* version, char* out_line, size_t out_size) {
cJSON* root = cJSON_CreateObject();
if (root == nullptr) {
return 0;
}
const bool status_added = cJSON_AddStringToObject(root, "status", "accepted") != nullptr;
cJSON* info = status_added ? cJSON_AddObjectToObject(root, "info") : nullptr;
const bool info_added = info != nullptr
&& cJSON_AddStringToObject(info, "vendor", vendor) != nullptr
&& cJSON_AddStringToObject(info, "name", name) != nullptr
&& cJSON_AddStringToObject(info, "id", id) != nullptr
&& cJSON_AddStringToObject(info, "version", version) != nullptr;
const bool printed = info_added && out_size >= 2 && cJSON_PrintPreallocated(root, out_line, static_cast<int>(out_size), false);
cJSON_Delete(root);
if (!printed) {
return 0;
}
const size_t length = strlen(out_line);
out_line[length] = '\n';
out_line[length + 1] = '\0';
return length + 1;
}

DevelopmentFrameParseResult development_frame_parse(const uint8_t* data, size_t size, DevelopmentFrame* out_frame, size_t* out_consumed) {
if (size < DEVELOPMENT_FRAME_HEADER_SIZE) {
return DevelopmentFrameParseResult::Incomplete;
Expand Down
7 changes: 7 additions & 0 deletions Modules/development-module/source/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,13 @@ void handleConnection(ServerData* data, int socket, const sockaddr_in& address)
return;
}

// The device information isn't sensitive, so it's handed out without a client certificate or trust
if (strcmp(session->hello.mode, "info") == 0) {
development_session_send_info(session, CONFIG_TT_DEVICE_VENDOR, CONFIG_TT_DEVICE_NAME_SIMPLE, CONFIG_TT_DEVICE_ID, TT_VERSION);
development_session_free(session);
return;
}

if (session->fingerprint[0] == '\0') {
LOG_W(TAG, "Client %s has no certificate", session->peer_address);
development_session_send_status(session, "error", "client certificate required");
Expand Down
6 changes: 6 additions & 0 deletions Modules/development-module/source/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ bool development_session_send_status(DevelopmentSession* session, const char* st
return length > 0 && development_session_write(session, line, length, STATUS_TIMEOUT_MS);
}

bool development_session_send_info(DevelopmentSession* session, const char* vendor, const char* name, const char* id, const char* version) {
char line[256];
const size_t length = development_protocol_format_info(vendor, name, id, version, line, sizeof(line));
return length > 0 && development_session_write(session, line, length, STATUS_TIMEOUT_MS);
}

bool development_session_send_frame(DevelopmentSession* session, DevelopmentFrameType type, const void* payload, uint16_t length) {
if (length > DEVELOPMENT_FRAME_PAYLOAD_MAX) {
return false;
Expand Down
Loading
Loading