Skip to content

Commit 20be607

Browse files
lhstrhjesslin02tanneberger
authored
Build support for the TypeScript target (#16)
* Work in progress toward NPM backend * Invoke lfc instead of rustc * add npm build * draft of backend for npm * fix format * update clean * add condition for run and build * refactored typescript target * rust fmt * select correct npm or pnpm backend * completely reworked the npm backend * working pnpm backend * fixing clippy * unified npm and pnpm backend more beutifully * removing print * formatting default ts example --------- Co-authored-by: jesslin02 <[email protected]> Co-authored-by: revol-xut <[email protected]>
1 parent ed352e8 commit 20be607

File tree

9 files changed

+327
-153
lines changed

9 files changed

+327
-153
lines changed

Cargo.lock

Lines changed: 153 additions & 151 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

defaults/HelloTS.lf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
target TypeScript
2+
3+
main reactor {
4+
reaction(startup) {= console.log("Hello World"); =}
5+
}

src/args.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::{Args, Parser, Subcommand};
33
use serde_derive::{Deserialize, Serialize};
44
use std::path::PathBuf;
55

6-
#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Serialize)]
6+
#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
77
pub enum TargetLanguage {
88
C,
99
Cpp,
@@ -23,6 +23,8 @@ pub enum BuildSystem {
2323
LFC,
2424
CMake,
2525
Cargo,
26+
Npm,
27+
Pnpm,
2628
}
2729

2830
#[derive(Args, Debug)]

src/backends/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use crate::{args::BuildSystem, package::App};
1111

1212
pub mod cmake;
1313
pub mod lfc;
14+
pub mod npm;
15+
pub mod pnpm;
1416

1517
pub fn execute_command<'a>(command: &CommandSpec, apps: &[&'a App]) -> BatchBuildResults<'a> {
1618
// Group apps by build system
@@ -39,6 +41,8 @@ pub fn execute_command<'a>(command: &CommandSpec, apps: &[&'a App]) -> BatchBuil
3941
BuildSystem::LFC => lfc::LFC.execute_command(command, &mut sub_res),
4042
BuildSystem::CMake => cmake::Cmake.execute_command(command, &mut sub_res),
4143
BuildSystem::Cargo => todo!(),
44+
BuildSystem::Npm => npm::Npm.execute_command(command, &mut sub_res),
45+
BuildSystem::Pnpm => pnpm::Pnpm.execute_command(command, &mut sub_res),
4246
};
4347
result.append(sub_res);
4448
}
@@ -64,6 +68,8 @@ pub struct BuildCommandOptions {
6468
/// that the number will be automatically determined.
6569
/// A value of one effectively disables parallel builds.
6670
pub max_threads: usize,
71+
/// if compilation should continue if one of the apps fails building
72+
pub keep_going: bool,
6773
}
6874

6975
/// Description of a lingo command

src/backends/npm.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use crate::util::errors::LingoError;
2+
use crate::util::execute_command_to_build_result;
3+
use std::error::Error;
4+
use std::fs;
5+
use std::path::{Path, PathBuf};
6+
use std::process::Command;
7+
8+
pub struct Npm;
9+
10+
pub struct TypeScriptToolCommands {
11+
pub binary_name: &'static str,
12+
pub install_command: &'static str,
13+
pub release_build_argument: &'static str,
14+
}
15+
16+
use crate::backends::{
17+
BatchBackend, BatchBuildResults, BuildCommandOptions, BuildProfile, CommandSpec,
18+
};
19+
20+
pub fn do_typescript_build(
21+
results: &mut BatchBuildResults,
22+
options: &BuildCommandOptions,
23+
commands: TypeScriptToolCommands,
24+
) {
25+
super::lfc::LFC::do_parallel_lfc_codegen(options, results, false);
26+
if !options.compile_target_code {
27+
return;
28+
}
29+
30+
let extract_name = |path: &PathBuf| -> Result<String, Box<dyn Error + Send + Sync>> {
31+
match Path::new(&path).file_stem() {
32+
Some(value) => value
33+
.to_str()
34+
.map(String::from)
35+
.ok_or(Box::new(LingoError::InvalidMainReactor)),
36+
None => Err(Box::new(LingoError::InvalidMainReactor)),
37+
}
38+
};
39+
40+
results
41+
.map(|app| {
42+
let file_name = extract_name(&app.main_reactor)?;
43+
let path = app.output_root.join("src-gen").join(file_name);
44+
45+
let mut npm_install = Command::new(commands.binary_name);
46+
npm_install.current_dir(path);
47+
npm_install.arg(commands.install_command);
48+
if options.profile == BuildProfile::Release {
49+
npm_install.arg(commands.release_build_argument);
50+
}
51+
execute_command_to_build_result(npm_install)?;
52+
Ok(())
53+
})
54+
.map(|app| {
55+
let file_name = extract_name(&app.main_reactor)?;
56+
let path = app.output_root.join("src-gen").join(file_name);
57+
58+
let mut npm_build = Command::new(commands.binary_name);
59+
npm_build.current_dir(path);
60+
npm_build.arg("run");
61+
npm_build.arg("build");
62+
63+
if options.profile == BuildProfile::Release {
64+
npm_build.arg(commands.release_build_argument);
65+
}
66+
execute_command_to_build_result(npm_build)?;
67+
68+
Ok(())
69+
})
70+
.map(|app| {
71+
fs::create_dir_all(app.output_root.join("bin"))?;
72+
73+
let file_name = extract_name(&app.main_reactor)?;
74+
let path = app
75+
.output_root
76+
.join("src-gen")
77+
.join(&file_name)
78+
.join("dist")
79+
.join(file_name + ".js");
80+
81+
// cleanup: rename executable to match the app name
82+
fs::rename(path, app.executable_path())?;
83+
Ok(())
84+
});
85+
}
86+
87+
impl BatchBackend for Npm {
88+
fn execute_command(&mut self, command: &CommandSpec, results: &mut BatchBuildResults) {
89+
match command {
90+
CommandSpec::Build(options) => do_typescript_build(
91+
results,
92+
options,
93+
TypeScriptToolCommands {
94+
binary_name: "npm",
95+
install_command: "install",
96+
release_build_argument: "--production",
97+
},
98+
),
99+
CommandSpec::Clean => {
100+
results.par_map(|app| {
101+
crate::util::default_build_clean(&app.output_root)?;
102+
crate::util::delete_subdirs(&app.output_root, &["node_modules", "dist"])?;
103+
Ok(())
104+
});
105+
}
106+
_ => todo!(),
107+
}
108+
}
109+
}

src/backends/pnpm.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use crate::backends::npm::{do_typescript_build, TypeScriptToolCommands};
2+
3+
pub struct Pnpm;
4+
5+
use crate::backends::{BatchBackend, BatchBuildResults, CommandSpec};
6+
7+
impl BatchBackend for Pnpm {
8+
fn execute_command(&mut self, command: &CommandSpec, results: &mut BatchBuildResults) {
9+
match command {
10+
CommandSpec::Build(options) => do_typescript_build(
11+
results,
12+
options,
13+
TypeScriptToolCommands {
14+
binary_name: "pnpm",
15+
install_command: "install",
16+
release_build_argument: "--prod",
17+
},
18+
),
19+
CommandSpec::Clean => {
20+
results.par_map(|app| {
21+
crate::util::default_build_clean(&app.output_root)?;
22+
crate::util::delete_subdirs(&app.output_root, &["node_modules", "dist"])?;
23+
Ok(())
24+
});
25+
}
26+
_ => todo!(),
27+
}
28+
}
29+
}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> {
119119
compile_target_code: !args.no_compile,
120120
lfc_exec_path: util::find_lfc_exec(args).expect("TODO replace me"),
121121
max_threads: args.threads,
122+
keep_going: args.keep_going,
122123
}),
123124
config,
124125
args.keep_going,

src/package/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::{env, io};
1313
use crate::args::BuildSystem::{CMake, Cargo, LFC};
1414
use crate::util::errors::{BuildResult, LingoError};
1515
use git2::Repository;
16+
use which::which;
1617

1718
fn is_valid_location_for_project(path: &std::path::Path) -> bool {
1819
!path.join("src").exists() && !path.join(".git").exists() && !path.join("application").exists()
@@ -93,6 +94,13 @@ impl App {
9394
match self.target {
9495
TargetLanguage::Cpp => CMake,
9596
TargetLanguage::Rust => Cargo,
97+
TargetLanguage::TypeScript => {
98+
if which("pnpm").is_ok() {
99+
BuildSystem::Pnpm
100+
} else {
101+
BuildSystem::Npm
102+
}
103+
}
96104
_ => LFC,
97105
}
98106
}
@@ -101,7 +109,11 @@ impl App {
101109
}
102110
pub fn executable_path(&self) -> PathBuf {
103111
let mut p = self.output_root.join("bin");
104-
p.push(&self.name);
112+
if self.target == TargetLanguage::TypeScript {
113+
p.push(self.name.clone() + ".js")
114+
} else {
115+
p.push(&self.name);
116+
}
105117
p
106118
}
107119
}
@@ -202,6 +214,7 @@ impl ConfigFile {
202214
let hello_world_code: &'static str = match self.apps[0].target {
203215
TargetLanguage::Cpp => include_str!("../../defaults/HelloCpp.lf"),
204216
TargetLanguage::C => include_str!("../../defaults/HelloC.lf"),
217+
TargetLanguage::TypeScript => include_str!("../../defaults/HelloTS.lf"),
205218
_ => panic!("Target langauge not supported yet"), // FIXME: Add examples for other programs
206219
};
207220

src/util/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub enum LingoError {
1515
UnknownAppNames(Vec<String>),
1616
InvalidProjectLocation(PathBuf),
1717
UseWestBuildToBuildApp,
18+
InvalidMainReactor,
1819
}
1920

2021
impl Display for LingoError {
@@ -35,6 +36,12 @@ impl Display for LingoError {
3536
LingoError::UseWestBuildToBuildApp => {
3637
write!(f, "Use `west lf-build` to build and run Zephyr programs.")
3738
}
39+
LingoError::InvalidMainReactor => {
40+
write!(
41+
f,
42+
"Not a valid path path to a file that contains a main reactor"
43+
)
44+
}
3845
}
3946
}
4047
}

0 commit comments

Comments
 (0)