diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..b2f0591 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +target = "thumbv6m-none-eabi" + +[unstable] +build-std = ["core", "panic_abort"] +build-std-features = ["panic_immediate_abort"] diff --git a/.gitignore b/.gitignore index 03f4a3c..7c73c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -.pio +/.pio/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f79dcf0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "usb3sun" +version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a9b00fe --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "usb3sun" +edition = "2021" +version = "2.0.0" +readme = "README.md" + +[lib] +path = "src/lib.rs" +name = "usb3sun" +edition = "2021" +crate-type = ["staticlib"] +required-features = [] diff --git a/platformio.cargo.py b/platformio.cargo.py new file mode 100644 index 0000000..3f5ce13 --- /dev/null +++ b/platformio.cargo.py @@ -0,0 +1,90 @@ +# Cargo <-> PlatformIO integration script (autogenerated by cargo-pio) +# Calling 'pio run' will also build the Rust library crate by invoking Cargo +# +# How to use: Insert/update the following line in one of platformio.ini's environments: +# extra_scripts = platformio.cargo.py + +import os + +Import("env") + +class Cargo: + def run(self, env): + self.__init_props(env) + + if self.__cargo_run_before_project: + # Attach as a pre-action to all source files so that in case CBindgen is used + # the C headers are generated before the files are compiled + env.AddPreAction(Glob(os.path.join(env.subst("$BUILD_DIR"), "src/*.o")), self.__run_cargo) + + # Hack. Need to always run when a C file from the src directory is built, or else the include directories + # passed to Cargo will not contain the includes coming from libraries imported with PlatformIO's Library Manager + env.AlwaysBuild(os.path.join(env.subst("$BUILD_DIR"), "src/dummy.o")) + + env.AddPreAction("$BUILD_DIR/$PROGNAME$PROGSUFFIX", [self.__run_cargo, self.__link_cargo]) + + def __init_props(self, env): + self.__cargo_ran = False + + self.__rust_lib = env.GetProjectOption("rust_lib") + self.__rust_target = env.GetProjectOption("rust_target") + + self.__rust_bindgen_enabled = env.GetProjectOption("rust_bindgen_enabled", default = "false").lower() == "true" + self.__rust_bindgen_extra_clang_args = env.GetProjectOption("rust_bindgen_extra_clang_args", default = "") + + self.__cargo_run_before_project = env.GetProjectOption("cargo_run_before_project", default = "false").lower() == "true" + self.__cargo_options = env.GetProjectOption("cargo_options", default = "") + self.__cargo_profile = env.GetProjectOption( + "cargo_profile", + default = "release" if env.GetProjectOption("build_type") == "release" else "debug") + self.__cargo_target_dir = env.GetProjectOption( + "cargo_target_dir", + default = os.path.join(env.subst("$PROJECT_BUILD_DIR"), "cargo") + if env.GetProjectOption("cargo_pio_common_build_dir", default = "").lower() == "true" + else os.path.join(env.subst("$PROJECT_DIR"), "target")) + + def __run_cargo(self, source, target, env): + if self.__cargo_ran: + return 0 + + print(">>> CARGO") + + board_mcu = env.get("BOARD_MCU") + if not board_mcu and "BOARD" in env: + board_mcu = env.BoardConfig().get("build.mcu") + + env["ENV"]["CARGO_BUILD_TARGET_DIR"] = self.__cargo_target_dir + env["ENV"]["CARGO_PIO_BUILD_PROJECT_DIR"] = env.subst("$PROJECT_DIR") + env["ENV"]["CARGO_PIO_BUILD_RELEASE_BUILD"] = str(env.GetProjectOption("build_type", default = "release") == "release") + + env["ENV"]["CARGO_PIO_BUILD_PATH"] = env["ENV"]["PATH"] + env["ENV"]["CARGO_PIO_BUILD_ACTIVE"] = "1" + env["ENV"]["CARGO_PIO_BUILD_INC_FLAGS"] = env.subst("$_CPPINCFLAGS") + env["ENV"]["CARGO_PIO_BUILD_LIB_FLAGS"] = env.subst("$_LIBFLAGS") + env["ENV"]["CARGO_PIO_BUILD_LIB_DIR_FLAGS"] = env.subst("$_LIBDIRFLAGS") + env["ENV"]["CARGO_PIO_BUILD_LIBS"] = env.subst("$LIBS") + env["ENV"]["CARGO_PIO_BUILD_LINK_FLAGS"] = env.subst("$LINKFLAGS") + env["ENV"]["CARGO_PIO_BUILD_LINK"] = env.subst("$LINK") + env["ENV"]["CARGO_PIO_BUILD_LINKCOM"] = env.subst("$LINKCOM") + env["ENV"]["CARGO_PIO_BUILD_MCU"] = board_mcu + + if self.__rust_bindgen_enabled: + env["ENV"]["CARGO_PIO_BUILD_BINDGEN_RUN"] = "True" + env["ENV"]["CARGO_PIO_BUILD_BINDGEN_EXTRA_CLANG_ARGS"] = self.__rust_bindgen_extra_clang_args + + env["ENV"]["CARGO_PIO_BUILD_PIO_PLATFORM_DIR"] = env.PioPlatform().get_dir()[0] + env["ENV"]["CARGO_PIO_BUILD_PIO_FRAMEWORK_DIR"] = env.PioPlatform().get_package_dir(env.PioPlatform().frameworks[env.GetProjectOption("framework")[0]]["package"]) + + self.__cargo_ran = True + result = env.Execute(f"cargo build {'--release' if self.__cargo_profile == 'release' else ''} --lib --target {self.__rust_target} {self.__cargo_options}") + + print("<<< CARGO") + + return result + + def __link_cargo(self, source, target, env): + env.Prepend(LINKFLAGS = ["-Wl,--allow-multiple-definition"]) # A hack to workaround this issue with Rust's compiler intrinsics: https://github.com/rust-lang/compiler-builtins/issues/353 + env.Prepend(LIBPATH = [env.subst(os.path.join(self.__cargo_target_dir, self.__rust_target, self.__cargo_profile))]) + env.Prepend(LIBS = [self.__rust_lib]) + +Cargo().run(env) diff --git a/platformio.git.py b/platformio.git.py new file mode 100644 index 0000000..16733e2 --- /dev/null +++ b/platformio.git.py @@ -0,0 +1,41 @@ +# A small PlatformIO integration script (autogenerated by cargo-pio) that provides the capability to clone +# user-specified Git projects (platformio.ini git_repos = "...") before the build is triggered +# +# How to use: +# Insert/update the following line in one of platformio.ini's environments: +# extra_scripts = pre:platformio.git.py +# Specify a newline-separated list of Git repos to check out: +# git_repos = [dir-name1]@ \n [dir-name2]@... + +import os + +Import("env") + +class GitRepos: + def run(self, env): + self.__git_repos = env.GetProjectOption("git_repos", default = "") + self.__materialize_git_repos() + + def __materialize_git_repos(self): + for (directory, repo) in self.__git_repos_list(): + if not os.path.exists(env.subst(directory)): + print(f"Cloning {repo} as directory {directory}") + if env.Execute(f"git clone {repo} {directory}"): + Exit(1) + + def __git_repos_list(self): + git_repos_dir = os.path.join("$PROJECT_WORKSPACE_DIR", "git-repos") + + def get_repo(item): + repo = item + repo_directory = repo.split("/")[-1] + if "@" in item: + repo_directory, repo = item.split("@", 1) + + return ( + os.path.join(git_repos_dir, repo_directory.strip()), + repo.strip()) + + return [get_repo(item) for item in self.__git_repos.split("\n") if len(item.strip()) > 0] + +GitRepos().run(env) diff --git a/platformio.ini b/platformio.ini index db484aa..9aad06c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,6 +8,12 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html +; cargo pio init -b pico -f arduino -t thumbv6m-none-eabi +[env] +extra_scripts = pre:platformio.git.py, pre:platformio.patch.py, platformio.cargo.py +rust_lib = usb3sun +rust_target = thumbv6m-none-eabi + [env:pico] platform = https://github.com/maxgerhardt/platform-raspberrypi.git board = pico diff --git a/platformio.patch.py b/platformio.patch.py new file mode 100644 index 0000000..0930dc7 --- /dev/null +++ b/platformio.patch.py @@ -0,0 +1,78 @@ +# A small PlatformIO integration script (autogenerated by cargo-pio) that provides the capability to patch +# PlatformIO packages (or the platform itself) before the build is triggered +# +# How to use: +# Insert/update the following line in one of platformio.ini's environments: +# extra_scripts = pre:platformio.patch.py +# Specify a newline-separated list of patches to apply: +# patches = @ \n @... +# ... where , etc. are expected to be placed in a 'patches/' folder of your project +# +# When is equal to "__platform__", the platform itself will be patched + +import os +import shutil + +Import("env") + +class Patch: + def run(self, env): + for (patch, patch_name, dir) in self.__patches_list(env): + self.__patch(env, patch, patch_name, dir) + + def __patch(self, env, patch, patch_name, dir): + patch_flag = os.path.join(dir, f"{patch_name}.applied") + + if not os.path.isfile(patch_flag): + # In recent PlatformIO, framework_espidf is no longer a true GIT repository + # (i.e. it does not contain a .git subfolder) + # As a result, "git apply" does not really work + # + # One workaround would've been to use `patch` instead of `git apply`, + # however `patch` does not seem to be available on Windows out of the box + # + # Therefore, instead we do a nasty hack here: we check if `dir` + # contains a `.git` folder, and if not we create it using "git init" + # + # Once the patch is applied, we remove the `.git` folder + git_dir = os.path.join(dir, ".git") + git_dir_exists = True + + if not os.path.exists(git_dir): + if env.Execute("git init", chdir = dir): + env.Exit(1) + git_dir_exists = False + + res = env.Execute(f"git apply {patch}", chdir = dir) + + if not git_dir_exists: + shutil.rmtree(git_dir) + + if res: + env.Exit(1) + + self.__touch(patch_flag) + + def __patches_list(self, env): + patches_dir = os.path.join(env.subst("$PROJECT_DIR"), "patches") + + def get_patch(item): + dir, patch = item.split("@", 1) + + package = dir.strip() + if package == "__platform__": + package_dir = env.PioPlatform().get_dir() + else: + package_dir = env.PioPlatform().get_package_dir(package) + + return ( + os.path.join(patches_dir, patch.strip()), + patch.strip(), + package_dir) + + return [get_patch(item) for item in env.GetProjectOption("patches", default = "").split("\n") if len(item.strip()) > 0] + + def __touch(self, path): + os.open(path, os.O_CREAT | os.O_RDWR) + +Patch().run(env) diff --git a/src/dummy.c b/src/dummy.c new file mode 100644 index 0000000..44abde6 --- /dev/null +++ b/src/dummy.c @@ -0,0 +1,10 @@ +// +// Cargo <-> PlatformIO helper C file (autogenerated by cargo-pio) +// This file is intentionally empty. Please DO NOT change it or delete it! +// +// Two reasons why this file is necessary: +// - PlatformIO complains if the src directory is empty with an error message +// 'Nothing to build. Please put your source code files to '../src' folder'. So we have to provide at least one C/C++ source file +// - The Cargo invocation is attached as a post-action to building this file. This is necessary, or else +// Cargo crates will not see the extra include directories of all libraries downloaded via the PlatformIO Library Manager +// diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..332e5b5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,20 @@ +// Remove if STD is supported for your platform and you plan to use it +#![no_std] + +// Remove if STD is supported for your platform and you plan to use it +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +// +// Entry points +// + +#[no_mangle] +extern "C" fn arduino_setup() { +} + +#[no_mangle] +extern "C" fn arduino_loop() { +}