mt-lang 0.2.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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tomlrb"
|
|
4
|
+
|
|
5
|
+
module MilkTea
|
|
6
|
+
class PackageManifestError < StandardError; end
|
|
7
|
+
|
|
8
|
+
class PackageManifest
|
|
9
|
+
DependencyView = Data.define(:name, :version, :version_req, :git, :git_rev, :git_subdir, :path) do
|
|
10
|
+
def path_dependency?
|
|
11
|
+
!path.nil?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def git_dependency?
|
|
15
|
+
!git.nil?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def registry?
|
|
19
|
+
!version_req.nil? && !path_dependency? && !git_dependency?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def exact_registry_version?
|
|
23
|
+
registry? && !version.nil?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
DataView = Data.define(:root_dir, :manifest_path, :package_name, :package_version, :package_kind, :source_root, :source_path, :profile, :platform, :output_path, :assets_paths, :html_template_path, :dependencies)
|
|
27
|
+
|
|
28
|
+
def self.default_package_name_for_root(root_dir)
|
|
29
|
+
snake_case_identifier(File.basename(File.expand_path(root_dir)))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.snake_case_identifier(name)
|
|
33
|
+
name.to_s
|
|
34
|
+
.gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
|
35
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
36
|
+
.tr("- ", "__")
|
|
37
|
+
.gsub(/_+/, "_")
|
|
38
|
+
.downcase
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.load(path)
|
|
42
|
+
new(path).load
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.manifest_exists_for?(path)
|
|
46
|
+
path = File.expand_path(path)
|
|
47
|
+
current = File.directory?(path) ? path : File.dirname(path)
|
|
48
|
+
loop do
|
|
49
|
+
return true if File.file?(File.join(current, "package.toml"))
|
|
50
|
+
parent = File.dirname(current)
|
|
51
|
+
break if parent == current
|
|
52
|
+
current = parent
|
|
53
|
+
end
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def initialize(path)
|
|
58
|
+
@path = File.expand_path(path)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def load
|
|
62
|
+
manifest_path, root_dir = resolve_manifest_path(@path)
|
|
63
|
+
raise PackageManifestError, "package manifest not found for #{@path}" unless manifest_path
|
|
64
|
+
|
|
65
|
+
config = parse_manifest(manifest_path)
|
|
66
|
+
package = config.fetch("package", {})
|
|
67
|
+
build = config.fetch("build", {})
|
|
68
|
+
profile_config = config.fetch("profile", {})
|
|
69
|
+
platform_config = config.fetch("platform", {})
|
|
70
|
+
|
|
71
|
+
package_name = package["name"]
|
|
72
|
+
if package_name.nil? || package_name.empty?
|
|
73
|
+
package_name = self.class.default_package_name_for_root(root_dir)
|
|
74
|
+
end
|
|
75
|
+
package_version = package["version"]
|
|
76
|
+
package_version = package_version.to_s unless package_version.nil?
|
|
77
|
+
package_version = nil if package_version == ""
|
|
78
|
+
|
|
79
|
+
package_kind = normalize_package_kind(package["kind"] || build["type"] || default_package_kind(build))
|
|
80
|
+
source_root_value = package["source_root"] || default_source_root(root_dir)
|
|
81
|
+
source_root = File.expand_path(source_root_value.to_s, root_dir)
|
|
82
|
+
unless File.directory?(source_root)
|
|
83
|
+
raise PackageManifestError, "package.source_root not found: #{source_root_value} (resolved to #{source_root})"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
profile_name = normalize_profile_name(build["profile"] || profile_config["default"])
|
|
87
|
+
platform_name = normalize_platform_name(build["platform"] || platform_config["default"])
|
|
88
|
+
|
|
89
|
+
source_path = resolve_source_path(root_dir, build, package_kind)
|
|
90
|
+
|
|
91
|
+
output_path = build["output"]
|
|
92
|
+
output_path = File.expand_path(output_path.to_s, root_dir) if output_path
|
|
93
|
+
|
|
94
|
+
assets_paths = parse_assets_paths(build["assets"], root_dir)
|
|
95
|
+
|
|
96
|
+
html_template_path = build["html_template"]
|
|
97
|
+
if html_template_path
|
|
98
|
+
html_template_path = File.expand_path(html_template_path.to_s, root_dir)
|
|
99
|
+
unless File.file?(html_template_path)
|
|
100
|
+
raise PackageManifestError, "build.html_template not found: #{build["html_template"]} (resolved to #{html_template_path})"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
dependencies = parse_dependencies(config.fetch("dependencies", {}), root_dir:, manifest_path:)
|
|
105
|
+
|
|
106
|
+
DataView.new(
|
|
107
|
+
root_dir:,
|
|
108
|
+
manifest_path:,
|
|
109
|
+
package_name:,
|
|
110
|
+
package_version:,
|
|
111
|
+
package_kind:,
|
|
112
|
+
source_root:,
|
|
113
|
+
source_path:,
|
|
114
|
+
profile: profile_name,
|
|
115
|
+
platform: platform_name,
|
|
116
|
+
output_path:,
|
|
117
|
+
assets_paths:,
|
|
118
|
+
html_template_path:,
|
|
119
|
+
dependencies:,
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
def parse_assets_paths(raw_assets, root_dir)
|
|
126
|
+
return [] unless raw_assets
|
|
127
|
+
|
|
128
|
+
asset_paths = (raw_assets.is_a?(Array) ? raw_assets : [raw_assets]).map do |raw_asset|
|
|
129
|
+
asset_path = File.expand_path(raw_asset.to_s, root_dir)
|
|
130
|
+
unless File.exist?(asset_path)
|
|
131
|
+
raise PackageManifestError, "build.assets not found: #{raw_asset} (resolved to #{asset_path})"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
asset_path
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
duplicate_basenames = asset_paths.group_by { |asset_path| File.basename(asset_path) }
|
|
138
|
+
.select { |_basename, paths| paths.length > 1 }
|
|
139
|
+
.keys
|
|
140
|
+
.sort
|
|
141
|
+
return asset_paths if duplicate_basenames.empty?
|
|
142
|
+
|
|
143
|
+
raise PackageManifestError, "build.assets entries must have unique basenames: #{duplicate_basenames.join(", ")}"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def resolve_manifest_path(path)
|
|
147
|
+
if File.directory?(path)
|
|
148
|
+
manifest_path = File.join(path, "package.toml")
|
|
149
|
+
raise PackageManifestError, "package manifest not found: #{manifest_path}" unless File.file?(manifest_path)
|
|
150
|
+
|
|
151
|
+
return [manifest_path, path]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
if File.basename(path) == "package.toml"
|
|
155
|
+
raise PackageManifestError, "package manifest not found: #{path}" unless File.file?(path)
|
|
156
|
+
|
|
157
|
+
return [path, File.dirname(path)]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
current = File.dirname(path)
|
|
161
|
+
loop do
|
|
162
|
+
manifest_path = File.join(current, "package.toml")
|
|
163
|
+
return [manifest_path, current] if File.file?(manifest_path)
|
|
164
|
+
|
|
165
|
+
parent = File.dirname(current)
|
|
166
|
+
break if parent == current
|
|
167
|
+
|
|
168
|
+
current = parent
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
[nil, nil]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def parse_manifest(path)
|
|
175
|
+
data = Tomlrb.load_file(path)
|
|
176
|
+
raise PackageManifestError, "package.toml root must be a table: #{path}" unless data.is_a?(Hash)
|
|
177
|
+
|
|
178
|
+
data
|
|
179
|
+
rescue StandardError => e
|
|
180
|
+
raise PackageManifestError, "invalid package.toml at #{path}: #{e.message}"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def default_package_kind(build)
|
|
184
|
+
"application"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def default_source_root(root_dir)
|
|
188
|
+
conventional_source_root = File.join(root_dir, "src")
|
|
189
|
+
return "src" if File.directory?(conventional_source_root)
|
|
190
|
+
|
|
191
|
+
"."
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def resolve_source_path(root_dir, build, package_kind)
|
|
195
|
+
return nil if package_kind == :library
|
|
196
|
+
|
|
197
|
+
if build["entry"]
|
|
198
|
+
entry = build["entry"].to_s
|
|
199
|
+
source_path = File.expand_path(entry, root_dir)
|
|
200
|
+
unless File.file?(source_path)
|
|
201
|
+
raise PackageManifestError, "build.entry not found: #{entry} (resolved to #{source_path})"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
return source_path
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
default_source_path = File.expand_path("src/main.mt", root_dir)
|
|
208
|
+
return default_source_path if File.file?(default_source_path)
|
|
209
|
+
|
|
210
|
+
requested_path = File.expand_path(@path)
|
|
211
|
+
return requested_path if File.file?(requested_path) && path_within_root?(requested_path, root_dir)
|
|
212
|
+
|
|
213
|
+
nil
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def path_within_root?(path, root_dir)
|
|
217
|
+
normalized_root = File.expand_path(root_dir)
|
|
218
|
+
path == normalized_root || path.start_with?(normalized_root + File::SEPARATOR)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def parse_dependencies(raw_dependencies, root_dir:, manifest_path:)
|
|
222
|
+
return [] if raw_dependencies.nil?
|
|
223
|
+
unless raw_dependencies.is_a?(Hash)
|
|
224
|
+
raise PackageManifestError, "dependencies must be a table in #{manifest_path}"
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
raw_dependencies.map do |name, spec|
|
|
228
|
+
parse_dependency(name, spec, root_dir:, manifest_path:)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def parse_dependency(name, spec, root_dir:, manifest_path:)
|
|
233
|
+
case spec
|
|
234
|
+
when String
|
|
235
|
+
return registry_dependency(name, spec, manifest_path:)
|
|
236
|
+
when Hash
|
|
237
|
+
version = normalize_optional_dependency_value(spec["version"])
|
|
238
|
+
git = normalize_optional_dependency_value(spec["git"])
|
|
239
|
+
git_rev = normalize_optional_dependency_value(spec["rev"])
|
|
240
|
+
git_subdir = normalize_optional_dependency_value(spec["subdir"])
|
|
241
|
+
path_value = normalize_optional_dependency_value(spec["path"])
|
|
242
|
+
|
|
243
|
+
if git_rev && !git
|
|
244
|
+
raise PackageManifestError, "dependency #{name} in #{manifest_path} declares rev without git"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
if git_subdir && !git
|
|
248
|
+
raise PackageManifestError, "dependency #{name} in #{manifest_path} declares subdir without git"
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
if version && git
|
|
252
|
+
raise PackageManifestError,
|
|
253
|
+
"dependency #{name} in #{manifest_path} cannot combine version with git resolution"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
declared_sources = []
|
|
257
|
+
declared_sources << "git" if git
|
|
258
|
+
declared_sources << "path" if path_value
|
|
259
|
+
declared_sources << "version" if version && !path_value && !git
|
|
260
|
+
|
|
261
|
+
if declared_sources.empty?
|
|
262
|
+
raise PackageManifestError, "dependency #{name} in #{manifest_path} must declare version, git, or path"
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
if declared_sources.length > 1
|
|
266
|
+
raise PackageManifestError,
|
|
267
|
+
"dependency #{name} in #{manifest_path} must choose exactly one of version, git, or path"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
if path_value
|
|
271
|
+
path = File.expand_path(path_value, root_dir)
|
|
272
|
+
unless File.directory?(path)
|
|
273
|
+
raise PackageManifestError, "dependency #{name} path not found: #{spec["path"]} (resolved to #{path})"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
version_req = version ? normalize_registry_dependency_requirement(version, name:, manifest_path:) : nil
|
|
277
|
+
exact_version = version_req&.exact_version&.to_s
|
|
278
|
+
return DependencyView.new(name.to_s, exact_version, version_req, nil, nil, nil, path)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
if git
|
|
282
|
+
unless git_rev
|
|
283
|
+
raise PackageManifestError,
|
|
284
|
+
"dependency #{name} in #{manifest_path} uses git resolution but is missing rev"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
return DependencyView.new(name.to_s, nil, nil, git, git_rev, git_subdir, nil)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
registry_dependency(name, version, manifest_path:)
|
|
291
|
+
else
|
|
292
|
+
raise PackageManifestError, "dependency #{name} in #{manifest_path} has unsupported type #{spec.class}"
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def registry_dependency(name, raw_requirement, manifest_path:)
|
|
297
|
+
version_req = normalize_registry_dependency_requirement(raw_requirement, name:, manifest_path:)
|
|
298
|
+
exact_version = version_req.exact_version&.to_s
|
|
299
|
+
|
|
300
|
+
DependencyView.new(name.to_s, exact_version, version_req, nil, nil, nil, nil)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def normalize_optional_dependency_value(value)
|
|
304
|
+
text = value.to_s.strip
|
|
305
|
+
text.empty? ? nil : text
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def normalize_registry_dependency_requirement(value, name:, manifest_path:)
|
|
309
|
+
text = normalize_optional_dependency_value(value)
|
|
310
|
+
if text.nil?
|
|
311
|
+
raise PackageManifestError, "dependency #{name} in #{manifest_path} must declare a version"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
PackageVersionReq.parse(text, label: "dependency #{name} in #{manifest_path} version requirement")
|
|
315
|
+
rescue PackageVersionError => e
|
|
316
|
+
raise PackageManifestError, e.message
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def normalize_package_kind(value)
|
|
320
|
+
case value.to_s
|
|
321
|
+
when "", "application", "app", "executable"
|
|
322
|
+
:application
|
|
323
|
+
when "library", "lib"
|
|
324
|
+
:library
|
|
325
|
+
else
|
|
326
|
+
raise PackageManifestError, "unknown package kind #{value}; expected application|library"
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def normalize_profile_name(value)
|
|
331
|
+
case value.to_s
|
|
332
|
+
when "", "debug", "dev"
|
|
333
|
+
:debug
|
|
334
|
+
when "release", "rel"
|
|
335
|
+
:release
|
|
336
|
+
else
|
|
337
|
+
raise PackageManifestError, "unknown profile #{value}; expected debug|release"
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def normalize_platform_name(value)
|
|
342
|
+
case value.to_s
|
|
343
|
+
when "", "linux"
|
|
344
|
+
:linux
|
|
345
|
+
when "windows", "win", "win32"
|
|
346
|
+
:windows
|
|
347
|
+
when "wasm", "web", "html5", "browser"
|
|
348
|
+
:wasm
|
|
349
|
+
when "darwin", "macos", "osx"
|
|
350
|
+
:darwin
|
|
351
|
+
else
|
|
352
|
+
raise PackageManifestError, "unknown platform #{value}; expected linux|windows|wasm|darwin"
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tomlrb"
|
|
4
|
+
|
|
5
|
+
module MilkTea
|
|
6
|
+
class PackageManifestEditorError < StandardError; end
|
|
7
|
+
|
|
8
|
+
class PackageManifestEditor
|
|
9
|
+
attr_reader :manifest_path
|
|
10
|
+
|
|
11
|
+
def initialize(path)
|
|
12
|
+
manifest = PackageManifest.load(path)
|
|
13
|
+
@manifest_path = manifest.manifest_path
|
|
14
|
+
rescue PackageManifestError => e
|
|
15
|
+
raise PackageManifestEditorError, e.message
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_dependency(name, raw_spec)
|
|
19
|
+
config = parse_manifest
|
|
20
|
+
dependencies = normalize_dependencies(config)
|
|
21
|
+
dependencies[name] = raw_spec
|
|
22
|
+
write_dependencies(dependencies)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def remove_dependency(name)
|
|
26
|
+
config = parse_manifest
|
|
27
|
+
dependencies = normalize_dependencies(config)
|
|
28
|
+
raise PackageManifestEditorError, "dependency #{name} not found in #{@manifest_path}" unless dependencies.key?(name)
|
|
29
|
+
|
|
30
|
+
dependencies.delete(name)
|
|
31
|
+
write_dependencies(dependencies)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def parse_manifest
|
|
37
|
+
data = Tomlrb.load_file(@manifest_path)
|
|
38
|
+
raise PackageManifestEditorError, "package.toml root must be a table: #{@manifest_path}" unless data.is_a?(Hash)
|
|
39
|
+
|
|
40
|
+
data
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
raise PackageManifestEditorError, "invalid package.toml at #{@manifest_path}: #{e.message}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def normalize_dependencies(config)
|
|
46
|
+
dependencies = config["dependencies"]
|
|
47
|
+
return {} if dependencies.nil?
|
|
48
|
+
raise PackageManifestEditorError, "dependencies must be a table in #{@manifest_path}" unless dependencies.is_a?(Hash)
|
|
49
|
+
|
|
50
|
+
dependencies.dup
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def write_dependencies(dependencies)
|
|
54
|
+
source = File.read(@manifest_path)
|
|
55
|
+
replacement = dependencies.empty? ? nil : render_dependencies_section(dependencies)
|
|
56
|
+
updated_source = replace_dependencies_section(source, replacement)
|
|
57
|
+
PackageAtomicWrite.write(@manifest_path, updated_source)
|
|
58
|
+
rescue SystemCallError => e
|
|
59
|
+
raise PackageManifestEditorError, "failed to update #{@manifest_path}: #{e.message}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def render_dependencies_section(dependencies)
|
|
63
|
+
lines = ["[dependencies]\n"]
|
|
64
|
+
dependencies.each do |name, spec|
|
|
65
|
+
lines << "#{toml_string(name)} = #{render_dependency_spec(spec)}\n"
|
|
66
|
+
end
|
|
67
|
+
lines.join
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def render_dependency_spec(spec)
|
|
71
|
+
case spec
|
|
72
|
+
when String
|
|
73
|
+
toml_string(spec)
|
|
74
|
+
when Hash
|
|
75
|
+
pairs = spec.map do |key, value|
|
|
76
|
+
"#{key} = #{toml_string(value)}"
|
|
77
|
+
end
|
|
78
|
+
"{ #{pairs.join(', ')} }"
|
|
79
|
+
else
|
|
80
|
+
raise PackageManifestEditorError, "unsupported dependency spec type #{spec.class}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def replace_dependencies_section(source, replacement)
|
|
85
|
+
lines = source.lines
|
|
86
|
+
start_index = lines.index { |line| line.match?(/^\[dependencies\]\s*$/) }
|
|
87
|
+
if start_index
|
|
88
|
+
end_index = lines[(start_index + 1)..]&.index { |line| line.start_with?("[") }
|
|
89
|
+
end_index = end_index ? start_index + 1 + end_index : lines.length
|
|
90
|
+
before = lines[0...start_index]
|
|
91
|
+
after = lines[end_index..] || []
|
|
92
|
+
return rebuild_source(before, replacement, after)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
return source if replacement.nil?
|
|
96
|
+
|
|
97
|
+
rebuilt = source.dup
|
|
98
|
+
rebuilt << "\n" unless rebuilt.empty? || rebuilt.end_with?("\n")
|
|
99
|
+
rebuilt << "\n" unless rebuilt.empty? || rebuilt.end_with?("\n\n")
|
|
100
|
+
rebuilt << replacement
|
|
101
|
+
rebuilt
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def rebuild_source(before, replacement, after)
|
|
105
|
+
rebuilt = before.join
|
|
106
|
+
rebuilt = rebuilt.rstrip
|
|
107
|
+
if replacement
|
|
108
|
+
rebuilt << "\n\n" unless rebuilt.empty?
|
|
109
|
+
rebuilt << replacement
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
trailing = after.join.lstrip
|
|
113
|
+
unless trailing.empty?
|
|
114
|
+
rebuilt << "\n" unless rebuilt.empty? || rebuilt.end_with?("\n")
|
|
115
|
+
rebuilt << "\n" unless rebuilt.end_with?("\n\n")
|
|
116
|
+
rebuilt << trailing
|
|
117
|
+
else
|
|
118
|
+
rebuilt << "\n" unless rebuilt.empty? || rebuilt.end_with?("\n")
|
|
119
|
+
end
|
|
120
|
+
rebuilt
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def toml_string(value)
|
|
124
|
+
escaped = value.to_s
|
|
125
|
+
.gsub('\\', '\\\\')
|
|
126
|
+
.gsub('"', '\\"')
|
|
127
|
+
.gsub("\n", '\\n')
|
|
128
|
+
.gsub("\r", '\\r')
|
|
129
|
+
.gsub("\t", '\\t')
|
|
130
|
+
|
|
131
|
+
%Q("#{escaped}")
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
class PackageRegistryMetadataProviderError < StandardError; end
|
|
5
|
+
|
|
6
|
+
class PackageRegistryMetadataProvider
|
|
7
|
+
def initialize(registry_store: PackageRegistryStore.new)
|
|
8
|
+
@registry_store = registry_store
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def available_versions(package_name)
|
|
12
|
+
@registry_store.available_versions(package_name).map do |version_text|
|
|
13
|
+
PackageVersion.parse(version_text, label: "registry package #{package_name} version")
|
|
14
|
+
end.sort.reverse
|
|
15
|
+
rescue PackageRegistryStoreError, PackageVersionError => e
|
|
16
|
+
raise PackageRegistryMetadataProviderError, e.message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def manifest_for(package_name, version)
|
|
20
|
+
version_text = version.to_s
|
|
21
|
+
package_root = @registry_store.published_root_for(package_name, version_text, sync: true)
|
|
22
|
+
|
|
23
|
+
manifest = PackageManifest.load(package_root)
|
|
24
|
+
if manifest.package_name != package_name || manifest.package_version != version_text
|
|
25
|
+
raise PackageRegistryMetadataProviderError,
|
|
26
|
+
"registry package at #{manifest.manifest_path} resolved to #{manifest.package_name}@#{manifest.package_version.inspect}; expected #{package_name}@#{version_text}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
manifest
|
|
30
|
+
rescue PackageManifestError, PackageRegistryStoreError => e
|
|
31
|
+
raise PackageRegistryMetadataProviderError, e.message
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|