1use camino::Utf8PathBuf;
2use gleam_core::{
3 Result,
4 analyse::TargetSupport,
5 build::{Codegen, Compile, Mode, Options, Target},
6 paths::ProjectPaths,
7};
8
9static ENTRYPOINT_FILENAME_POWERSHELL: &str = "entrypoint.ps1";
10static ENTRYPOINT_FILENAME_POSIX_SHELL: &str = "entrypoint.sh";
11
12static ENTRYPOINT_TEMPLATE_POWERSHELL: &str =
13 include_str!("../templates/erlang-shipment-entrypoint.ps1");
14static ENTRYPOINT_TEMPLATE_POSIX_SHELL: &str =
15 include_str!("../templates/erlang-shipment-entrypoint.sh");
16
17// TODO: start in embedded mode
18// TODO: test
19
20/// Generate a directory of precompiled Erlang along with a start script.
21/// Suitable for deployment to a server.
22///
23/// For each Erlang application (aka package) directory these directories are
24/// copied across:
25/// - ebin
26/// - include
27/// - priv
28pub(crate) fn erlang_shipment(paths: &ProjectPaths) -> Result<()> {
29 let target = Target::Erlang;
30 let mode = Mode::Prod;
31 let build = paths.build_directory_for_target(mode, target);
32 let out = paths.erlang_shipment_directory();
33
34 crate::fs::mkdir(&out)?;
35
36 // Reset the directories to ensure we have a clean slate and no old code
37 crate::fs::delete_directory(&build)?;
38 crate::fs::delete_directory(&out)?;
39
40 // Build project in production mode
41 let built = crate::build::main(
42 paths,
43 Options {
44 root_target_support: TargetSupport::Enforced,
45 warnings_as_errors: false,
46 codegen: Codegen::All,
47 compile: Compile::All,
48 mode,
49 target: Some(target),
50 no_print_progress: false,
51 },
52 crate::build::download_dependencies(paths, crate::cli::Reporter::new())?,
53 )?;
54
55 for entry in crate::fs::read_dir(&build)?.filter_map(Result::ok) {
56 let path = entry.path();
57
58 // We are only interested in package directories
59 if !path.is_dir() {
60 continue;
61 }
62
63 let name = path.file_name().expect("Directory name");
64 let build = build.join(name);
65 let out = out.join(name);
66 crate::fs::mkdir(&out)?;
67
68 // Copy desired package subdirectories
69 for subdirectory in ["ebin", "priv", "include"] {
70 let source = build.join(subdirectory);
71 if source.is_dir() {
72 let source = crate::fs::canonicalise(&source)?;
73 let out = out.join(subdirectory);
74 crate::fs::copy_dir(source, &out)?;
75 }
76 }
77 }
78
79 // PowerShell entry point script.
80 write_entrypoint_script(
81 &out.join(ENTRYPOINT_FILENAME_POWERSHELL),
82 ENTRYPOINT_TEMPLATE_POWERSHELL,
83 &built.root_package.config.name,
84 )?;
85
86 // POSIX Shell entry point script.
87 write_entrypoint_script(
88 &out.join(ENTRYPOINT_FILENAME_POSIX_SHELL),
89 ENTRYPOINT_TEMPLATE_POSIX_SHELL,
90 &built.root_package.config.name,
91 )?;
92
93 crate::cli::print_exported(&built.root_package.config.name);
94
95 println!(
96 "
97Your Erlang shipment has been generated to {out}.
98
99It can be copied to a compatible server with Erlang installed and run with
100one of the following scripts:
101 - {ENTRYPOINT_FILENAME_POWERSHELL} (PowerShell script)
102 - {ENTRYPOINT_FILENAME_POSIX_SHELL} (POSIX Shell script)
103",
104 );
105
106 Ok(())
107}
108
109fn write_entrypoint_script(
110 entrypoint_output_path: &Utf8PathBuf,
111 entrypoint_template_path: &str,
112 package_name: &str,
113) -> Result<(), gleam_core::Error> {
114 let text = entrypoint_template_path.replace("$PACKAGE_NAME_FROM_GLEAM", package_name);
115 crate::fs::write(entrypoint_output_path, &text)?;
116 crate::fs::make_executable(entrypoint_output_path)?;
117 Ok(())
118}
119
120pub fn hex_tarball(paths: &ProjectPaths) -> Result<()> {
121 let mut config = crate::config::root_config(paths)?;
122 let data: Vec<u8> = crate::publish::build_hex_tarball(paths, &mut config)?;
123
124 let path = paths.build_export_hex_tarball(&config.name, &config.version.to_string());
125 crate::fs::write_bytes(&path, &data)?;
126 println!(
127 "
128Your hex tarball has been generated in {}.
129",
130 &path
131 );
132 Ok(())
133}
134
135pub fn javascript_prelude() -> Result<()> {
136 print!("{}", gleam_core::javascript::PRELUDE);
137 Ok(())
138}
139
140pub fn typescript_prelude() -> Result<()> {
141 print!("{}", gleam_core::javascript::PRELUDE_TS_DEF);
142 Ok(())
143}
144
145pub fn package_interface(paths: &ProjectPaths, out: Utf8PathBuf) -> Result<()> {
146 // Build the project
147 let mut built = crate::build::main(
148 paths,
149 Options {
150 mode: Mode::Prod,
151 target: None,
152 codegen: Codegen::All,
153 compile: Compile::All,
154 warnings_as_errors: false,
155 root_target_support: TargetSupport::Enforced,
156 no_print_progress: false,
157 },
158 crate::build::download_dependencies(paths, crate::cli::Reporter::new())?,
159 )?;
160 built.root_package.attach_doc_and_module_comments();
161
162 let out = gleam_core::docs::generate_json_package_interface(
163 out,
164 &built.root_package,
165 &built.module_interfaces,
166 );
167 crate::fs::write_outputs_under(&[out], paths.root())?;
168 Ok(())
169}
170
171pub fn package_information(paths: &ProjectPaths, out: Utf8PathBuf) -> Result<()> {
172 let config = crate::config::root_config(paths)?;
173 let out = gleam_core::docs::generate_json_package_information(out, config);
174 crate::fs::write_outputs_under(&[out], paths.root())?;
175 Ok(())
176}