From 9fc60e104ff7cdafdb18633958dca37faac4783a Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 5 Jun 2020 13:32:56 +0200 Subject: [PATCH] Add another way to load .60 files from a build script --- Cargo.toml | 2 + api/sixtyfps-rs/lib.rs | 74 ++++++++++++++++++++++ api/sixtyfps-rs/sixtyfps-build/Cargo.toml | 12 ++++ api/sixtyfps-rs/sixtyfps-build/lib.rs | 77 +++++++++++++++++++++++ examples/rusttest2/Cargo.toml | 12 ++++ examples/rusttest2/build.rs | 3 + examples/rusttest2/src/main.rs | 16 +++++ sixtyfps_compiler/lib.rs | 8 ++- sixtyfps_runtime/corelib/lib.rs | 9 +++ sixtyfps_runtime/interpreter/lib.rs | 8 +++ 10 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 api/sixtyfps-rs/sixtyfps-build/Cargo.toml create mode 100644 api/sixtyfps-rs/sixtyfps-build/lib.rs create mode 100644 examples/rusttest2/Cargo.toml create mode 100644 examples/rusttest2/build.rs create mode 100644 examples/rusttest2/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 465b42031..de52e8f4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,14 @@ members = [ 'sixtyfps_compiler/parser_test_macro', 'api/sixtyfps-rs', 'api/sixtyfps-rs/sixtyfps-rs-macro', + 'api/sixtyfps-rs/sixtyfps-build', 'api/sixtyfps-node/native', 'tools/compiler', 'tools/viewer', 'tools/syntax_updater', 'examples/graphicstest', 'examples/rusttest', + 'examples/rusttest2', 'examples/rustwasmtest', 'helper_crates/const-field-offset', 'helper_crates/vtable', diff --git a/api/sixtyfps-rs/lib.rs b/api/sixtyfps-rs/lib.rs index 9d618d7d2..083e33608 100644 --- a/api/sixtyfps-rs/lib.rs +++ b/api/sixtyfps-rs/lib.rs @@ -1,6 +1,72 @@ +/*! +# SixtyFPS + +This create is the main entry point for project using SixtyFPS UI in rust. + +## How to use: + +There are two ways to use this crate. + + - The `.60` code inline in a macro. + - The `.60` code in external files compiled with `build.rs` + +### The .60 code in a macro + +This is the simpler way, just put the + +```rust +sixtyfps::sixtyfps!{ + HelloWolrd := Text { text: "hello world"; } +} +fn main() { +# return; // Don't run a window in an example + HelloWolrd::default().run() +} +``` + +### The .60 file in external files compiled with `build.rs` + +In your Cargo.toml: + +FIXME! set the version + +```toml +[package] +... +build = "build.rs" + +[dependencies] +sixtyfps = "*" +... + +[build-dependencies] +sixtyfps-build = "*" +``` + +In the `build.rs` file: + +```ignore +fn main() { + sixtyfps_build::compile("ui/hello.60"); +} +``` + +Then in your main file + +```ignore +sixtyfps::include_modules!(); +fn main() { + HelloWolrd::default().run() +} +``` +*/ + +#![warn(missing_docs)] + pub use sixtyfps_rs_macro::sixtyfps; /// internal re_exports used by the macro generated +#[doc(hidden)] pub mod re_exports { pub use const_field_offset::{self, FieldOffsets}; pub use corelib::abi::datastructures::{Component, ComponentTO, ComponentVTable, ItemTreeNode}; @@ -17,3 +83,11 @@ pub mod re_exports { #[cfg(doctest)] mod compile_fail_tests; + +/// Include the code generated with the sixtyfps-build crate from the build script +#[macro_export] +macro_rules! include_modules { + () => { + include!(env!("SIXTYFPS_INCLUDE_GENERATED")); + }; +} diff --git a/api/sixtyfps-rs/sixtyfps-build/Cargo.toml b/api/sixtyfps-rs/sixtyfps-build/Cargo.toml new file mode 100644 index 000000000..5e7b8a376 --- /dev/null +++ b/api/sixtyfps-rs/sixtyfps-build/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sixtyfps-build" +version = "0.1.0" +authors = ["Sixty FPS "] +edition = "2018" + +[lib] +path = "lib.rs" + +[dependencies] +sixtyfps_compilerlib = { path = "../../../sixtyfps_compiler", features = ["rust", "display-diagnostics"] } +thiserror = "1" diff --git a/api/sixtyfps-rs/sixtyfps-build/lib.rs b/api/sixtyfps-rs/sixtyfps-build/lib.rs new file mode 100644 index 000000000..665a2da70 --- /dev/null +++ b/api/sixtyfps-rs/sixtyfps-build/lib.rs @@ -0,0 +1,77 @@ +/*! + This crate serves as a compagnon crate for the sixtyfps crate. + It is meant to be able to compile the `.60` files from your `build.rs`script + + The main entry point of this crate is the gernerate() function +*/ + +#![warn(missing_docs)] + +use sixtyfps_compilerlib::*; +use std::env; +use std::io::Write; +use std::path::Path; + +/// Error returned by the `compile` function +#[derive(thiserror::Error, Debug)] +pub enum CompileError { + /// Cannot read environment variable CARGO_MANIFEST_DIR or OUT_DIR. The build script need to be run via cargo. + #[error("Cannot read environment variable CARGO_MANIFEST_DIR or OUT_DIR. The build script need to be run via cargo.")] + NotRunViaCargo, + /// Cannot load the input .60 file + #[error("Cannot load the .60 file: {0}")] + LoadError(std::io::Error), + /// Parse error. The error are printed in the stderr, and also are in the vector + #[error("{0:?}")] + CompileError(Vec), + /// Cannot write the generated file + #[error("Cannot load the .60 file: {0}")] + SaveError(std::io::Error), +} + +/// Compile the `.60` file and generate rust code for it. +/// +/// The path is relative to the `CARGO_MANIFEST_DIR`. +/// +/// The following line need to be added within your crate to include the generated code. +/// ```ignore +/// sixtyfps::include_modules!(); +/// ``` +pub fn compile(path: impl AsRef) -> Result<(), CompileError> { + let path = Path::new(&env::var_os("CARGO_MANIFEST_DIR").ok_or(CompileError::NotRunViaCargo)?) + .join(path.as_ref()); + + let source = std::fs::read_to_string(&path).map_err(CompileError::LoadError)?; + let (syntax_node, mut diag) = parser::parse(&source); + diag.current_path = path.clone(); + + if diag.has_error() { + let vec = diag.inner.iter().map(|d| d.message.clone()).collect(); + diag.print(source); + return Err(CompileError::CompileError(vec)); + } + + let mut tr = typeregister::TypeRegister::builtin(); + let doc = object_tree::Document::from_node(syntax_node, &mut diag, &mut tr); + run_passes(&doc, &mut diag, &mut tr); + + if diag.has_error() { + let vec = diag.inner.iter().map(|d| d.message.clone()).collect(); + diag.print(source); + return Err(CompileError::CompileError(vec)); + } + + let output_file_path = Path::new(&env::var_os("OUT_DIR").ok_or(CompileError::NotRunViaCargo)?) + .join(path.file_name().map(Path::new).unwrap_or(Path::new("sixtyfps_out.rs"))); + + let mut file = std::fs::File::create(&output_file_path).map_err(CompileError::SaveError)?; + let generated = generator::rust::generate(&doc.root_component, &mut diag).ok_or_else(|| { + let vec = diag.inner.iter().map(|d| d.message.clone()).collect(); + diag.print(source); + CompileError::CompileError(vec) + })?; + write!(file, "{}", generated).map_err(CompileError::SaveError)?; + println!("cargo:rerun-if-changed={}", path.to_string_lossy()); + println!("cargo:rustc-env=SIXTYFPS_INCLUDE_GENERATED={}", output_file_path.to_string_lossy()); + Ok(()) +} diff --git a/examples/rusttest2/Cargo.toml b/examples/rusttest2/Cargo.toml new file mode 100644 index 000000000..128f5f4ed --- /dev/null +++ b/examples/rusttest2/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rusttest2" +version = "0.1.0" +authors = ["Sixty FPS "] +edition = "2018" +build = "build.rs" + +[dependencies] +sixtyfps = { path = "../../api/sixtyfps-rs" } + +[build-dependencies] +sixtyfps-build = { path = "../../api/sixtyfps-rs/sixtyfps-build" } diff --git a/examples/rusttest2/build.rs b/examples/rusttest2/build.rs new file mode 100644 index 000000000..642f4d538 --- /dev/null +++ b/examples/rusttest2/build.rs @@ -0,0 +1,3 @@ +fn main() { + sixtyfps_build::compile("../cpptest/hello.60").unwrap(); +} diff --git a/examples/rusttest2/src/main.rs b/examples/rusttest2/src/main.rs new file mode 100644 index 000000000..589feda3d --- /dev/null +++ b/examples/rusttest2/src/main.rs @@ -0,0 +1,16 @@ +sixtyfps::include_modules!(); + +fn main() { + let mut app = Hello::default(); + + app.plus_clicked.set_handler(|context, ()| { + let app = context.component.downcast::().unwrap(); + app.counter.set(app.counter.get(context) + 1); + }); + app.minus_clicked.set_handler(|context, ()| { + let app = context.component.downcast::().unwrap(); + app.counter.set(app.counter.get(context) - 1); + }); + + app.run(); +} diff --git a/sixtyfps_compiler/lib.rs b/sixtyfps_compiler/lib.rs index d0eddecba..1f80b68c1 100644 --- a/sixtyfps_compiler/lib.rs +++ b/sixtyfps_compiler/lib.rs @@ -1,7 +1,11 @@ /*! -# The SixtyFPS compiler +# The SixtyFPS compiler library -The different modules tage the source code and transform into data structures +**NOTE:** This library is an internal crate for the SixtyFPS project. +This crate should not be used directly by application using SixtyFPS. +You should use the `sixtyfps` crate instead + +The different modules take the source code and transform into data structures according to the following schema ```text diff --git a/sixtyfps_runtime/corelib/lib.rs b/sixtyfps_runtime/corelib/lib.rs index 10bece64a..96ebdccab 100644 --- a/sixtyfps_runtime/corelib/lib.rs +++ b/sixtyfps_runtime/corelib/lib.rs @@ -1,3 +1,12 @@ +/*! + +# SixtyFPS runtime library + +**NOTE:** This library is an internal crate for the SixtyFPS project. +This crate should not be used directly by application using SixtyFPS. +You should use the `sixtyfps` crate instead +*/ + pub mod graphics; pub mod input; pub mod layout; diff --git a/sixtyfps_runtime/interpreter/lib.rs b/sixtyfps_runtime/interpreter/lib.rs index 23ac8e674..a466c0e09 100644 --- a/sixtyfps_runtime/interpreter/lib.rs +++ b/sixtyfps_runtime/interpreter/lib.rs @@ -1,3 +1,11 @@ +/*! +# SixtyFPS interpreter library + +**NOTE:** This library is an internal crate for the SixtyFPS project. +This crate should not be used directly by application using SixtyFPS. +You should use the `sixtyfps` crate instead +*/ + mod dynamic_component; mod dynamic_type; mod eval;