@tishlang/tish 1.9.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +8 -6
- package/crates/js_to_tish/src/transform/stmt.rs +12 -13
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cargo_native_registry.rs +4 -1
- package/crates/tish/src/main.rs +11 -8
- package/crates/tish/tests/integration_test.rs +145 -7
- package/crates/tish_ast/src/ast.rs +3 -9
- package/crates/tish_build_utils/src/lib.rs +43 -15
- package/crates/tish_builtins/src/array.rs +2 -3
- package/crates/tish_builtins/src/construct.rs +15 -28
- package/crates/tish_builtins/src/globals.rs +18 -16
- package/crates/tish_builtins/src/helpers.rs +1 -4
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/object.rs +10 -10
- package/crates/tish_builtins/src/string.rs +1 -3
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_compile/src/codegen.rs +123 -138
- package/crates/tish_compile/src/lib.rs +25 -3
- package/crates/tish_compile/src/resolve.rs +6 -3
- package/crates/tish_compile/src/types.rs +6 -6
- package/crates/tish_compile_js/src/codegen.rs +50 -29
- package/crates/tish_compile_js/src/tests_jsx.rs +44 -0
- package/crates/tish_core/src/console_style.rs +9 -0
- package/crates/tish_core/src/json.rs +17 -7
- package/crates/tish_core/src/macros.rs +2 -2
- package/crates/tish_core/src/value.rs +192 -4
- package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
- package/crates/tish_eval/src/eval.rs +135 -73
- package/crates/tish_eval/src/http.rs +18 -12
- package/crates/tish_eval/src/lib.rs +29 -0
- package/crates/tish_eval/src/regex.rs +1 -1
- package/crates/tish_eval/src/value.rs +89 -4
- package/crates/tish_eval/src/value_convert.rs +30 -8
- package/crates/tish_fmt/src/lib.rs +4 -1
- package/crates/tish_lexer/src/lib.rs +7 -2
- package/crates/tish_llvm/src/lib.rs +2 -2
- package/crates/tish_lsp/src/builtin_goto.rs +111 -10
- package/crates/tish_lsp/src/import_goto.rs +35 -22
- package/crates/tish_lsp/src/main.rs +118 -85
- package/crates/tish_native/src/build.rs +187 -10
- package/crates/tish_native/src/lib.rs +92 -8
- package/crates/tish_parser/src/lib.rs +77 -0
- package/crates/tish_parser/src/parser.rs +71 -74
- package/crates/tish_pg/src/error.rs +1 -1
- package/crates/tish_pg/src/lib.rs +61 -73
- package/crates/tish_resolve/src/lib.rs +283 -158
- package/crates/tish_resolve/src/pos.rs +10 -2
- package/crates/tish_runtime/Cargo.toml +3 -0
- package/crates/tish_runtime/src/http.rs +39 -39
- package/crates/tish_runtime/src/http_fetch.rs +12 -12
- package/crates/tish_runtime/src/lib.rs +26 -43
- package/crates/tish_runtime/src/native_promise.rs +0 -11
- package/crates/tish_runtime/src/promise.rs +14 -1
- package/crates/tish_runtime/src/promise_io.rs +1 -4
- package/crates/tish_runtime/src/ws.rs +40 -27
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
- package/crates/tish_ui/src/jsx.rs +6 -4
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_ui/src/runtime/hooks.rs +5 -15
- package/crates/tish_ui/src/runtime/mod.rs +16 -17
- package/crates/tish_vm/Cargo.toml +2 -0
- package/crates/tish_vm/src/vm.rs +218 -153
- package/crates/tish_wasm/src/lib.rs +33 -7
- package/crates/tish_wasm_runtime/Cargo.toml +4 -1
- package/crates/tish_wasm_runtime/src/lib.rs +2 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
- package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
- package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
- package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
- package/justfile +3 -3
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -8,15 +8,15 @@ use regex::Regex;
|
|
|
8
8
|
use tower_lsp::jsonrpc::Result;
|
|
9
9
|
use tower_lsp::lsp_types::{
|
|
10
10
|
CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse,
|
|
11
|
-
CompletionTriggerKind, Diagnostic, DiagnosticSeverity, DiagnosticTag,
|
|
12
|
-
DidCloseTextDocumentParams, DidOpenTextDocumentParams,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
CompletionTriggerKind, Diagnostic, DiagnosticSeverity, DiagnosticTag,
|
|
12
|
+
DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
|
|
13
|
+
DocumentFormattingParams, DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse,
|
|
14
|
+
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams,
|
|
15
|
+
HoverProviderCapability, InitializeParams, InitializeResult, Location, MarkupContent,
|
|
16
|
+
MarkupKind, MessageType, NumberOrString, OneOf, Position, Range, ReferenceParams,
|
|
17
|
+
RenameOptions, RenameParams, ServerCapabilities, ServerInfo, SymbolInformation, SymbolKind,
|
|
18
|
+
SymbolTag, TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, Url,
|
|
19
|
+
WorkDoneProgressOptions, WorkspaceEdit, WorkspaceSymbolParams,
|
|
20
20
|
};
|
|
21
21
|
use tower_lsp::lsp_types::{PrepareRenameResponse, TextEdit};
|
|
22
22
|
use tower_lsp::{Client, LanguageServer, LspService, Server};
|
|
@@ -78,6 +78,49 @@ fn diag_range(line: u32, col: u32, text: &str) -> Range {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/// `lsp-types` still requires the `deprecated` field on these structs, but marks it
|
|
82
|
+
/// `#[deprecated(note = "Use tags instead")]`. Use `tags` with [`SymbolTag::Deprecated`] when a
|
|
83
|
+
/// symbol is actually deprecated; this helper keeps a single `#[allow(deprecated)]` boundary.
|
|
84
|
+
#[allow(deprecated)]
|
|
85
|
+
fn symbol_information(
|
|
86
|
+
name: String,
|
|
87
|
+
kind: SymbolKind,
|
|
88
|
+
tags: Option<Vec<SymbolTag>>,
|
|
89
|
+
location: Location,
|
|
90
|
+
container_name: Option<String>,
|
|
91
|
+
) -> SymbolInformation {
|
|
92
|
+
SymbolInformation {
|
|
93
|
+
name,
|
|
94
|
+
kind,
|
|
95
|
+
tags,
|
|
96
|
+
deprecated: None,
|
|
97
|
+
location,
|
|
98
|
+
container_name,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#[allow(deprecated)]
|
|
103
|
+
fn document_symbol(
|
|
104
|
+
name: String,
|
|
105
|
+
detail: Option<String>,
|
|
106
|
+
kind: SymbolKind,
|
|
107
|
+
tags: Option<Vec<SymbolTag>>,
|
|
108
|
+
range: Range,
|
|
109
|
+
selection_range: Range,
|
|
110
|
+
children: Option<Vec<DocumentSymbol>>,
|
|
111
|
+
) -> DocumentSymbol {
|
|
112
|
+
DocumentSymbol {
|
|
113
|
+
name,
|
|
114
|
+
detail,
|
|
115
|
+
kind,
|
|
116
|
+
tags,
|
|
117
|
+
deprecated: None,
|
|
118
|
+
range,
|
|
119
|
+
selection_range,
|
|
120
|
+
children,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
81
124
|
fn publish_parse_and_lint(client: &Client, uri: Url, text: &str) {
|
|
82
125
|
let mut diags = Vec::new();
|
|
83
126
|
match tishlang_parser::parse(text) {
|
|
@@ -115,10 +158,7 @@ fn publish_parse_and_lint(client: &Client, uri: Url, text: &str) {
|
|
|
115
158
|
"tish-unused-parameter",
|
|
116
159
|
),
|
|
117
160
|
tishlang_resolve::UnusedBindingKind::Variable => (
|
|
118
|
-
format!(
|
|
119
|
-
"`{}` is declared but its value is never read",
|
|
120
|
-
ub.name
|
|
121
|
-
),
|
|
161
|
+
format!("`{}` is declared but its value is never read", ub.name),
|
|
122
162
|
"tish-unused-variable",
|
|
123
163
|
),
|
|
124
164
|
};
|
|
@@ -317,7 +357,7 @@ impl LanguageServer for Backend {
|
|
|
317
357
|
return Ok(None);
|
|
318
358
|
};
|
|
319
359
|
|
|
320
|
-
let mut syms: Vec<
|
|
360
|
+
let mut syms: Vec<DocumentSymbol> = Vec::new();
|
|
321
361
|
for s in &program.statements {
|
|
322
362
|
doc_symbol_stmt(s, &text, &mut syms);
|
|
323
363
|
}
|
|
@@ -432,7 +472,9 @@ impl LanguageServer for Backend {
|
|
|
432
472
|
}
|
|
433
473
|
None => {
|
|
434
474
|
if tishlang_resolve::is_runtime_global_ident(use_site.name.as_ref()) {
|
|
435
|
-
md.push_str(
|
|
475
|
+
md.push_str(
|
|
476
|
+
"\n\n_Interpreter root global (no lexical declaration in this file)._",
|
|
477
|
+
);
|
|
436
478
|
let word = word_at_position(&text, pos);
|
|
437
479
|
if !word.is_empty() {
|
|
438
480
|
if let Some(root) = self.tishlang_source_root.read().unwrap().clone() {
|
|
@@ -583,12 +625,8 @@ impl LanguageServer for Backend {
|
|
|
583
625
|
else {
|
|
584
626
|
return Ok(None);
|
|
585
627
|
};
|
|
586
|
-
let spans =
|
|
587
|
-
&program,
|
|
588
|
-
&text,
|
|
589
|
-
nu.name.as_ref(),
|
|
590
|
-
def,
|
|
591
|
-
);
|
|
628
|
+
let spans =
|
|
629
|
+
tishlang_resolve::reference_spans_for_def(&program, &text, nu.name.as_ref(), def);
|
|
592
630
|
let mut edits: Vec<TextEdit> = spans
|
|
593
631
|
.into_iter()
|
|
594
632
|
.map(|sp| TextEdit {
|
|
@@ -598,10 +636,8 @@ impl LanguageServer for Backend {
|
|
|
598
636
|
.collect();
|
|
599
637
|
// Apply from end of document so earlier ranges stay valid when lengths change.
|
|
600
638
|
edits.sort_by(|a, b| {
|
|
601
|
-
(b.range.start.line, b.range.start.character)
|
|
602
|
-
a.range.start.line,
|
|
603
|
-
a.range.start.character,
|
|
604
|
-
))
|
|
639
|
+
(b.range.start.line, b.range.start.character)
|
|
640
|
+
.cmp(&(a.range.start.line, a.range.start.character))
|
|
605
641
|
});
|
|
606
642
|
let mut m = HashMap::new();
|
|
607
643
|
m.insert(uri, edits);
|
|
@@ -689,41 +725,35 @@ fn collect_workspace_syms(
|
|
|
689
725
|
) {
|
|
690
726
|
match s {
|
|
691
727
|
tishlang_ast::Statement::FunDecl {
|
|
692
|
-
name,
|
|
693
|
-
name_span,
|
|
694
|
-
..
|
|
728
|
+
name, name_span, ..
|
|
695
729
|
} => {
|
|
696
730
|
if name.to_lowercase().contains(query) {
|
|
697
|
-
out.push(
|
|
698
|
-
name
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
location: Location {
|
|
731
|
+
out.push(symbol_information(
|
|
732
|
+
name.to_string(),
|
|
733
|
+
SymbolKind::FUNCTION,
|
|
734
|
+
None,
|
|
735
|
+
Location {
|
|
703
736
|
uri: uri.clone(),
|
|
704
737
|
range: span_to_range(name_span, text),
|
|
705
738
|
},
|
|
706
|
-
|
|
707
|
-
|
|
739
|
+
None,
|
|
740
|
+
));
|
|
708
741
|
}
|
|
709
742
|
}
|
|
710
743
|
tishlang_ast::Statement::VarDecl {
|
|
711
|
-
name,
|
|
712
|
-
name_span,
|
|
713
|
-
..
|
|
744
|
+
name, name_span, ..
|
|
714
745
|
} => {
|
|
715
746
|
if name.to_lowercase().contains(query) {
|
|
716
|
-
out.push(
|
|
717
|
-
name
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
location: Location {
|
|
747
|
+
out.push(symbol_information(
|
|
748
|
+
name.to_string(),
|
|
749
|
+
SymbolKind::VARIABLE,
|
|
750
|
+
None,
|
|
751
|
+
Location {
|
|
722
752
|
uri: uri.clone(),
|
|
723
753
|
range: span_to_range(name_span, text),
|
|
724
754
|
},
|
|
725
|
-
|
|
726
|
-
|
|
755
|
+
None,
|
|
756
|
+
));
|
|
727
757
|
}
|
|
728
758
|
}
|
|
729
759
|
tishlang_ast::Statement::Block { statements, .. } => {
|
|
@@ -744,9 +774,7 @@ pub(crate) fn find_export(
|
|
|
744
774
|
for s in &program.statements {
|
|
745
775
|
match s {
|
|
746
776
|
tishlang_ast::Statement::FunDecl {
|
|
747
|
-
name: n,
|
|
748
|
-
name_span,
|
|
749
|
-
..
|
|
777
|
+
name: n, name_span, ..
|
|
750
778
|
} if n.as_ref() == name => {
|
|
751
779
|
return Some(Location {
|
|
752
780
|
uri: uri.clone(),
|
|
@@ -754,9 +782,7 @@ pub(crate) fn find_export(
|
|
|
754
782
|
});
|
|
755
783
|
}
|
|
756
784
|
tishlang_ast::Statement::VarDecl {
|
|
757
|
-
name: n,
|
|
758
|
-
name_span,
|
|
759
|
-
..
|
|
785
|
+
name: n, name_span, ..
|
|
760
786
|
} if n.as_ref() == name => {
|
|
761
787
|
return Some(Location {
|
|
762
788
|
uri: uri.clone(),
|
|
@@ -785,17 +811,13 @@ fn find_decl_in_stmt(
|
|
|
785
811
|
) -> Option<Location> {
|
|
786
812
|
match s {
|
|
787
813
|
tishlang_ast::Statement::FunDecl {
|
|
788
|
-
name,
|
|
789
|
-
name_span,
|
|
790
|
-
..
|
|
814
|
+
name, name_span, ..
|
|
791
815
|
} if name.as_ref() == word => Some(Location {
|
|
792
816
|
uri: uri.clone(),
|
|
793
817
|
range: span_to_range(name_span, text),
|
|
794
818
|
}),
|
|
795
819
|
tishlang_ast::Statement::VarDecl {
|
|
796
|
-
name,
|
|
797
|
-
name_span,
|
|
798
|
-
..
|
|
820
|
+
name, name_span, ..
|
|
799
821
|
} if name.as_ref() == word => Some(Location {
|
|
800
822
|
uri: uri.clone(),
|
|
801
823
|
range: span_to_range(name_span, text),
|
|
@@ -915,15 +937,26 @@ fn value_completion_kind_stmt(
|
|
|
915
937
|
finally_body,
|
|
916
938
|
..
|
|
917
939
|
} => value_completion_kind_stmt(body, name)
|
|
918
|
-
.or_else(||
|
|
919
|
-
|
|
940
|
+
.or_else(|| {
|
|
941
|
+
catch_body
|
|
942
|
+
.as_ref()
|
|
943
|
+
.and_then(|b| value_completion_kind_stmt(b, name))
|
|
944
|
+
})
|
|
945
|
+
.or_else(|| {
|
|
946
|
+
finally_body
|
|
947
|
+
.as_ref()
|
|
948
|
+
.and_then(|b| value_completion_kind_stmt(b, name))
|
|
949
|
+
}),
|
|
920
950
|
tishlang_ast::Statement::Switch {
|
|
921
951
|
cases,
|
|
922
952
|
default_body,
|
|
923
953
|
..
|
|
924
954
|
} => {
|
|
925
955
|
for (_e, stmts) in cases {
|
|
926
|
-
if let Some(k) = stmts
|
|
956
|
+
if let Some(k) = stmts
|
|
957
|
+
.iter()
|
|
958
|
+
.find_map(|st| value_completion_kind_stmt(st, name))
|
|
959
|
+
{
|
|
927
960
|
return Some(k);
|
|
928
961
|
}
|
|
929
962
|
}
|
|
@@ -934,7 +967,9 @@ fn value_completion_kind_stmt(
|
|
|
934
967
|
})
|
|
935
968
|
}
|
|
936
969
|
tishlang_ast::Statement::Export { declaration, .. } => match declaration.as_ref() {
|
|
937
|
-
tishlang_ast::ExportDeclaration::Named(inner) =>
|
|
970
|
+
tishlang_ast::ExportDeclaration::Named(inner) => {
|
|
971
|
+
value_completion_kind_stmt(inner, name)
|
|
972
|
+
}
|
|
938
973
|
tishlang_ast::ExportDeclaration::Default(_) => None,
|
|
939
974
|
},
|
|
940
975
|
_ => None,
|
|
@@ -944,7 +979,7 @@ fn value_completion_kind_stmt(
|
|
|
944
979
|
fn doc_symbol_stmt(
|
|
945
980
|
s: &tishlang_ast::Statement,
|
|
946
981
|
text: &str,
|
|
947
|
-
out: &mut Vec<
|
|
982
|
+
out: &mut Vec<DocumentSymbol>,
|
|
948
983
|
) {
|
|
949
984
|
match s {
|
|
950
985
|
tishlang_ast::Statement::FunDecl {
|
|
@@ -956,20 +991,19 @@ fn doc_symbol_stmt(
|
|
|
956
991
|
} => {
|
|
957
992
|
let mut children = Vec::new();
|
|
958
993
|
collect_child_syms(body, text, &mut children);
|
|
959
|
-
out.push(
|
|
960
|
-
name
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
children: if children.is_empty() {
|
|
994
|
+
out.push(document_symbol(
|
|
995
|
+
name.to_string(),
|
|
996
|
+
None,
|
|
997
|
+
SymbolKind::FUNCTION,
|
|
998
|
+
None,
|
|
999
|
+
span_to_range(span, text),
|
|
1000
|
+
span_to_range(name_span, text),
|
|
1001
|
+
if children.is_empty() {
|
|
968
1002
|
None
|
|
969
1003
|
} else {
|
|
970
1004
|
Some(children)
|
|
971
1005
|
},
|
|
972
|
-
|
|
1006
|
+
));
|
|
973
1007
|
}
|
|
974
1008
|
tishlang_ast::Statement::VarDecl {
|
|
975
1009
|
name,
|
|
@@ -977,16 +1011,15 @@ fn doc_symbol_stmt(
|
|
|
977
1011
|
span,
|
|
978
1012
|
..
|
|
979
1013
|
} => {
|
|
980
|
-
out.push(
|
|
981
|
-
name
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
});
|
|
1014
|
+
out.push(document_symbol(
|
|
1015
|
+
name.to_string(),
|
|
1016
|
+
None,
|
|
1017
|
+
SymbolKind::VARIABLE,
|
|
1018
|
+
None,
|
|
1019
|
+
span_to_range(span, text),
|
|
1020
|
+
span_to_range(name_span, text),
|
|
1021
|
+
None,
|
|
1022
|
+
));
|
|
990
1023
|
}
|
|
991
1024
|
tishlang_ast::Statement::Block { statements, .. } => {
|
|
992
1025
|
for x in statements {
|
|
@@ -1000,7 +1033,7 @@ fn doc_symbol_stmt(
|
|
|
1000
1033
|
fn collect_child_syms(
|
|
1001
1034
|
s: &tishlang_ast::Statement,
|
|
1002
1035
|
text: &str,
|
|
1003
|
-
out: &mut Vec<
|
|
1036
|
+
out: &mut Vec<DocumentSymbol>,
|
|
1004
1037
|
) {
|
|
1005
1038
|
match s {
|
|
1006
1039
|
tishlang_ast::Statement::Block { statements, .. } => {
|
|
@@ -6,8 +6,15 @@ use std::path::Path;
|
|
|
6
6
|
use tishlang_compile::ResolvedNativeModule;
|
|
7
7
|
|
|
8
8
|
/// `tishlang_runtime` Cargo feature names (subset of CLI / compile feature names).
|
|
9
|
-
const RUNTIME_CARGO_FEATURES: &[&str] =
|
|
10
|
-
|
|
9
|
+
const RUNTIME_CARGO_FEATURES: &[&str] = &[
|
|
10
|
+
"http",
|
|
11
|
+
"http-hyper",
|
|
12
|
+
"http-io-uring",
|
|
13
|
+
"fs",
|
|
14
|
+
"process",
|
|
15
|
+
"regex",
|
|
16
|
+
"ws",
|
|
17
|
+
];
|
|
11
18
|
|
|
12
19
|
/// Map CLI/compile features to flags passed to `tishlang_runtime` in the temp crate's Cargo.toml.
|
|
13
20
|
/// `full` enables every optional runtime capability (matches `tish build --feature full` / LANGUAGE.md).
|
|
@@ -29,6 +36,29 @@ fn runtime_features_for_cargo(features: &[String]) -> Vec<String> {
|
|
|
29
36
|
out
|
|
30
37
|
}
|
|
31
38
|
|
|
39
|
+
/// `[profile.release]` for nested `cargo build` of generated crates.
|
|
40
|
+
fn nested_release_profile_toml() -> &'static str {
|
|
41
|
+
if std::env::var("TISH_FAST_NATIVE_BUILD").as_deref() == Ok("1") {
|
|
42
|
+
r#"[profile.release]
|
|
43
|
+
opt-level = 1
|
|
44
|
+
lto = false
|
|
45
|
+
codegen-units = 16
|
|
46
|
+
incremental = true
|
|
47
|
+
strip = false
|
|
48
|
+
debug = 0
|
|
49
|
+
panic = "abort"
|
|
50
|
+
"#
|
|
51
|
+
} else {
|
|
52
|
+
r#"[profile.release]
|
|
53
|
+
# Reduce binary size: strip symbols, abort on panic (no unwinding), single codegen unit
|
|
54
|
+
strip = true
|
|
55
|
+
panic = "abort"
|
|
56
|
+
codegen-units = 1
|
|
57
|
+
lto = "fat"
|
|
58
|
+
"#
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
32
62
|
/// Inject `mod generated_native;` after the crate attribute so the binary crate can call `crate::generated_native::…`.
|
|
33
63
|
fn inject_generated_native_mod(rust_code: &str) -> String {
|
|
34
64
|
if let Some(pos) = rust_code.find("\n\n") {
|
|
@@ -108,6 +138,7 @@ pub fn build_via_cargo(
|
|
|
108
138
|
String::new()
|
|
109
139
|
};
|
|
110
140
|
|
|
141
|
+
let profile = nested_release_profile_toml();
|
|
111
142
|
let cargo_toml = format!(
|
|
112
143
|
r#"[package]
|
|
113
144
|
name = "tish_output"
|
|
@@ -118,17 +149,12 @@ edition = "2021"
|
|
|
118
149
|
name = "{}"
|
|
119
150
|
path = "src/main.rs"
|
|
120
151
|
|
|
121
|
-
|
|
122
|
-
# Reduce binary size: strip symbols, abort on panic (no unwinding), single codegen unit
|
|
123
|
-
strip = true
|
|
124
|
-
panic = "abort"
|
|
125
|
-
codegen-units = 1
|
|
126
|
-
lto = "fat"
|
|
152
|
+
{}
|
|
127
153
|
|
|
128
154
|
[dependencies]
|
|
129
155
|
tishlang_runtime = {{ path = {:?}{} }}
|
|
130
156
|
{}{}"#,
|
|
131
|
-
out_name, runtime_path, features_str, more_deps, ui_dep
|
|
157
|
+
out_name, profile, runtime_path, features_str, more_deps, ui_dep
|
|
132
158
|
);
|
|
133
159
|
|
|
134
160
|
fs::write(build_dir.join("Cargo.toml"), cargo_toml)
|
|
@@ -159,6 +185,155 @@ tishlang_runtime = {{ path = {:?}{} }}
|
|
|
159
185
|
Ok(())
|
|
160
186
|
}
|
|
161
187
|
|
|
188
|
+
/// Build several native binaries in **one** nested Cargo project (shared `tishlang_runtime` compile).
|
|
189
|
+
///
|
|
190
|
+
/// `bins` order must match `outputs`: each `(stem, rust_code, generated_native_rs)` pairs with
|
|
191
|
+
/// `outputs[i].0` (entry path — used only for validation) and `outputs[i].1` (final binary path).
|
|
192
|
+
pub(crate) fn build_many_via_cargo(
|
|
193
|
+
bins: Vec<(String, String, Option<String>)>,
|
|
194
|
+
native_modules: Vec<ResolvedNativeModule>,
|
|
195
|
+
features: &[String],
|
|
196
|
+
extra_dependencies_toml: &str,
|
|
197
|
+
needs_tokio: bool,
|
|
198
|
+
needs_ui: bool,
|
|
199
|
+
outputs: &[(&Path, &Path)],
|
|
200
|
+
project_root: Option<&Path>,
|
|
201
|
+
) -> Result<(), String> {
|
|
202
|
+
if bins.len() != outputs.len() {
|
|
203
|
+
return Err(format!(
|
|
204
|
+
"build_many_via_cargo: bins ({}) != outputs ({})",
|
|
205
|
+
bins.len(),
|
|
206
|
+
outputs.len()
|
|
207
|
+
));
|
|
208
|
+
}
|
|
209
|
+
for (i, (stem, _, _)) in bins.iter().enumerate() {
|
|
210
|
+
let entry = outputs[i].0;
|
|
211
|
+
let expect = entry.file_stem().and_then(|s| s.to_str()).unwrap_or("");
|
|
212
|
+
if expect != stem {
|
|
213
|
+
return Err(format!(
|
|
214
|
+
"build_many_via_cargo: stem mismatch at {}: {} vs {}",
|
|
215
|
+
i, stem, expect
|
|
216
|
+
));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let batch_id = format!("many_{}", std::process::id());
|
|
221
|
+
let build_dir = tishlang_build_utils::create_build_dir("tish_build_many", &batch_id)?;
|
|
222
|
+
|
|
223
|
+
let runtime_path = tishlang_build_utils::find_runtime_path_for_project(project_root)?;
|
|
224
|
+
|
|
225
|
+
let runtime_features = runtime_features_for_cargo(features);
|
|
226
|
+
let runtime_refs: Vec<&str> = runtime_features.iter().map(String::as_str).collect();
|
|
227
|
+
let features_str = if runtime_refs.is_empty() {
|
|
228
|
+
String::new()
|
|
229
|
+
} else {
|
|
230
|
+
format!(", features = {:?}", runtime_refs)
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
let tokio_dep = if needs_tokio {
|
|
234
|
+
"\ntokio = { version = \"1\", features = [\"rt-multi-thread\", \"macros\"] }\n"
|
|
235
|
+
} else {
|
|
236
|
+
""
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
let native_deps: String = native_modules
|
|
240
|
+
.iter()
|
|
241
|
+
.filter(|m| m.use_path_dependency)
|
|
242
|
+
.map(|m| {
|
|
243
|
+
let path = m.crate_path.display().to_string().replace('\\', "/");
|
|
244
|
+
format!("{} = {{ path = {:?} }}\n", m.package_name, path)
|
|
245
|
+
})
|
|
246
|
+
.collect();
|
|
247
|
+
|
|
248
|
+
let mut more_deps = String::new();
|
|
249
|
+
more_deps.push_str(tokio_dep);
|
|
250
|
+
if !native_deps.is_empty() {
|
|
251
|
+
more_deps.push_str(&format!("\n{}", native_deps));
|
|
252
|
+
}
|
|
253
|
+
if !extra_dependencies_toml.trim().is_empty() {
|
|
254
|
+
more_deps.push_str(&format!("\n{}", extra_dependencies_toml));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let tish_ui_path = std::path::Path::new(&runtime_path)
|
|
258
|
+
.parent()
|
|
259
|
+
.ok_or_else(|| "invalid tishlang_runtime path (no parent)".to_string())?
|
|
260
|
+
.join("tish_ui");
|
|
261
|
+
let ui_dep = if needs_ui {
|
|
262
|
+
format!(
|
|
263
|
+
"\ntishlang_ui = {{ path = {:?}, default-features = false, features = [\"runtime\"] }}\n",
|
|
264
|
+
tish_ui_path.display().to_string().replace('\\', "/")
|
|
265
|
+
)
|
|
266
|
+
} else {
|
|
267
|
+
String::new()
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
let mut bin_tables = String::new();
|
|
271
|
+
for (stem, rust_code, generated_native_rs) in &bins {
|
|
272
|
+
let bin_dir = build_dir.join("src/bin").join(stem);
|
|
273
|
+
fs::create_dir_all(&bin_dir).map_err(|e| format!("create bin dir: {}", e))?;
|
|
274
|
+
|
|
275
|
+
let rust_main = if generated_native_rs.is_some() {
|
|
276
|
+
inject_generated_native_mod(rust_code)
|
|
277
|
+
} else {
|
|
278
|
+
rust_code.clone()
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
fs::write(bin_dir.join("main.rs"), rust_main)
|
|
282
|
+
.map_err(|e| format!("write main.rs for {}: {}", stem, e))?;
|
|
283
|
+
if let Some(gen) = generated_native_rs {
|
|
284
|
+
fs::write(bin_dir.join("generated_native.rs"), gen)
|
|
285
|
+
.map_err(|e| format!("write generated_native.rs for {}: {}", stem, e))?;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
bin_tables.push_str(&format!(
|
|
289
|
+
r#"[[bin]]
|
|
290
|
+
name = "{stem}"
|
|
291
|
+
path = "src/bin/{stem}/main.rs"
|
|
292
|
+
|
|
293
|
+
"#
|
|
294
|
+
));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let profile = nested_release_profile_toml();
|
|
298
|
+
let cargo_toml = format!(
|
|
299
|
+
r#"[package]
|
|
300
|
+
name = "tish_output_many"
|
|
301
|
+
version = "0.1.0"
|
|
302
|
+
edition = "2021"
|
|
303
|
+
|
|
304
|
+
{}{}
|
|
305
|
+
[dependencies]
|
|
306
|
+
tishlang_runtime = {{ path = {:?}{} }}
|
|
307
|
+
{}{}"#,
|
|
308
|
+
bin_tables, profile, runtime_path, features_str, more_deps, ui_dep
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
fs::write(build_dir.join("Cargo.toml"), cargo_toml)
|
|
312
|
+
.map_err(|e| format!("Cannot write Cargo.toml: {}", e))?;
|
|
313
|
+
|
|
314
|
+
let workspace_target = Path::new(&runtime_path)
|
|
315
|
+
.parent()
|
|
316
|
+
.and_then(|p| p.parent())
|
|
317
|
+
.map(|ws| ws.join("target"));
|
|
318
|
+
let target_dir = workspace_target.filter(|p| p.exists());
|
|
319
|
+
let binary_dir = target_dir
|
|
320
|
+
.as_ref()
|
|
321
|
+
.map(|t| t.join("release"))
|
|
322
|
+
.unwrap_or_else(|| build_dir.join("target").join("release"));
|
|
323
|
+
|
|
324
|
+
tishlang_build_utils::run_cargo_build(&build_dir, target_dir.as_deref())?;
|
|
325
|
+
|
|
326
|
+
for i in 0..bins.len() {
|
|
327
|
+
let stem = bins[i].0.as_str();
|
|
328
|
+
let output_path = outputs[i].1;
|
|
329
|
+
let binary = tishlang_build_utils::find_release_binary(binary_dir.as_path(), stem)?;
|
|
330
|
+
let target = tishlang_build_utils::resolve_output_path(output_path, stem);
|
|
331
|
+
tishlang_build_utils::copy_binary_to_output(&binary, &target)?;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
Ok(())
|
|
335
|
+
}
|
|
336
|
+
|
|
162
337
|
#[cfg(test)]
|
|
163
338
|
mod tests {
|
|
164
339
|
use super::runtime_features_for_cargo;
|
|
@@ -176,6 +351,8 @@ mod tests {
|
|
|
176
351
|
#[test]
|
|
177
352
|
fn runtime_features_merges_full_and_specific() {
|
|
178
353
|
let f = runtime_features_for_cargo(&["full".to_string(), "http".to_string()]);
|
|
179
|
-
|
|
354
|
+
// `full` expands to every RUNTIME_CARGO_FEATURES entry; redundant `http` must not duplicate.
|
|
355
|
+
assert_eq!(f.len(), super::RUNTIME_CARGO_FEATURES.len());
|
|
356
|
+
assert_eq!(f.iter().filter(|x| *x == "http").count(), 1);
|
|
180
357
|
}
|
|
181
358
|
}
|