|
| 1 | +defmodule BuildScript do |
| 2 | + @url "https://github.com/V-Sekai/world-editor/releases/download/latest.v-sekai-editor-187/v-sekai-world.zip" |
| 3 | + @output "v-sekai-world.zip" |
| 4 | + @extract_dir "export" |
| 5 | + @extract_dir_extracted "#{@extract_dir}/temp" |
| 6 | + @os_family :os.type() |
| 7 | + @project_name "xr_grid" |
| 8 | + @expected_hash "F7AB549A676C52DD47F9D74C9308EBBE447ABFA1513C7E563B3C15809C0CE3E3" |
| 9 | + |
| 10 | + def all do |
| 11 | + pending_files_to_delete = editor_download() |
| 12 | + editor_sign() |
| 13 | + export_stage() |
| 14 | + cleanup_pending_files(pending_files_to_delete) |
| 15 | + end |
| 16 | + def editor_download do |
| 17 | + case verify_hash() do |
| 18 | + {:ok, _hash, _message} -> |
| 19 | + IO.puts("File hash matches, skipping download.") |
| 20 | + {:error, _hash, _message} -> |
| 21 | + download() |
| 22 | + case verify_hash() do |
| 23 | + {:ok, _hash, _message} -> |
| 24 | + IO.puts("Download complete and hash verified.") |
| 25 | + {:error, _hash, _message} -> |
| 26 | + IO.puts("Hash verification failed after download.") |
| 27 | + exit(:normal) |
| 28 | + end |
| 29 | + end |
| 30 | + pending_files_to_delete = extract() |
| 31 | + create_gdignore() |
| 32 | + pending_files_to_delete |
| 33 | + end |
| 34 | + |
| 35 | + defp cleanup_pending_files(files) do |
| 36 | + IO.inspect(files, label: "Pending files to delete") |
| 37 | + end |
| 38 | + |
| 39 | + def editor_sign do |
| 40 | + make_executable() |
| 41 | + sign_app() |
| 42 | + end |
| 43 | + |
| 44 | + def download do |
| 45 | + File.mkdir_p!(@extract_dir) |
| 46 | + System.cmd("curl", ["-L", @url, "-o", "#{@extract_dir}/#{@output}"]) |
| 47 | + end |
| 48 | + |
| 49 | + def verify_hash do |
| 50 | + case File.read("#{@extract_dir}/#{@output}") do |
| 51 | + {:ok, content} -> |
| 52 | + hash = :crypto.hash(:sha256, content) |> Base.encode16() |
| 53 | + IO.puts("Computed hash: #{hash}") |
| 54 | + if hash == @expected_hash do |
| 55 | + {:ok, hash, "Hash matches"} |
| 56 | + else |
| 57 | + {:error, hash, "Hash does not match"} |
| 58 | + end |
| 59 | + _ -> |
| 60 | + {:error, nil, "File not found"} |
| 61 | + end |
| 62 | + end |
| 63 | + |
| 64 | + def extract do |
| 65 | + File.mkdir_p!(@extract_dir_extracted) |
| 66 | + case System.cmd("tar", ["-xvf", "#{@extract_dir}/#{@output}", "-C", "#{@extract_dir_extracted}", "--strip-components=1"]) do |
| 67 | + {output, 0} -> |
| 68 | + IO.puts("Extraction output:\n#{output}") |
| 69 | + extracted_files = Path.wildcard("#{@extract_dir_extracted}/**/*") |
| 70 | + extracted_files |
| 71 | + {output, _} -> |
| 72 | + IO.puts("Failed to extract file. Output:\n#{output}") |
| 73 | + exit(:normal) |
| 74 | + end |
| 75 | + end |
| 76 | + |
| 77 | + def create_gdignore do |
| 78 | + File.touch("#{@extract_dir}/.gdignore") |
| 79 | + end |
| 80 | + |
| 81 | + def make_executable do |
| 82 | + case @os_family do |
| 83 | + {:win32, :nt} -> |
| 84 | + # Windows specific commands |
| 85 | + :ok |
| 86 | + {:unix, _} -> |
| 87 | + File.chmod("#{@extract_dir_extracted}/godot.macos.editor.double.arm64", 0o755) |
| 88 | + File.chmod("#{@extract_dir_extracted}/godot.macos.template_debug.double.arm64", 0o755) |
| 89 | + File.chmod("#{@extract_dir_extracted}/godot.macos.template_release.double.arm64", 0o755) |
| 90 | + File.chmod("#{@extract_dir_extracted}/godot_macos_editor_double.app/Contents/MacOS/Godot", 0o755) |
| 91 | + end |
| 92 | + end |
| 93 | + |
| 94 | + def sign_app do |
| 95 | + case @os_family do |
| 96 | + {:win32, :nt} -> |
| 97 | + # Windows specific commands |
| 98 | + :ok |
| 99 | + {:unix, :darwin} -> |
| 100 | + System.cmd("codesign", ["--deep", "--force", "--sign", "-", "#{@extract_dir_extracted}/godot.macos.editor.double.arm64"]) |
| 101 | + System.cmd("codesign", ["--deep", "--force", "--sign", "-", "#{@extract_dir_extracted}/godot.macos.template_debug.double.arm64"]) |
| 102 | + System.cmd("codesign", ["--deep", "--force", "--sign", "-", "#{@extract_dir_extracted}/godot.macos.template_release.double.arm64"]) |
| 103 | + System.cmd("codesign", ["--deep", "--force", "--sign", "-", "#{@extract_dir_extracted}/godot_macos_editor_double.app/Contents/MacOS/Godot"]) |
| 104 | + :ok |
| 105 | + _ -> |
| 106 | + :ok |
| 107 | + end |
| 108 | + end |
| 109 | + |
| 110 | + def export_stage do |
| 111 | + editor_path = case @os_family do |
| 112 | + {:win32, :nt} -> |
| 113 | + "#{@extract_dir_extracted}/godot.windows.editor.double.x86_64.llvm.exe" |
| 114 | + {:unix, :darwin} -> |
| 115 | + "#{@extract_dir_extracted}/godot.macos.editor.double.arm64" |
| 116 | + {:unix, _} -> |
| 117 | + "#{@extract_dir_extracted}/godot.linux.editor.double.x86_64" |
| 118 | + end |
| 119 | + |
| 120 | + if File.exists?(editor_path) do |
| 121 | + File.chmod(editor_path, 0o755) |
| 122 | + version = System.cmd(editor_path, ["--version"]) |> elem(0) |> String.trim() |
| 123 | + platforms = [ |
| 124 | + {"windows", "x86_64"}, |
| 125 | + {"linuxbsd", "x86_64"}, |
| 126 | + {"macos", "arm64"}, |
| 127 | + {"web", "wasm32"}, |
| 128 | + ] |
| 129 | + for {target_platform, target_arch} <- platforms do |
| 130 | + export_template(platform_name(@os_family), platform_arch(@os_family), target_platform, target_arch, version) |
| 131 | + export_platform(platform_name(@os_family), platform_arch(@os_family), target_platform, target_arch) |
| 132 | + end |
| 133 | + else |
| 134 | + IO.puts("Editor path not found: #{editor_path}") |
| 135 | + end |
| 136 | + end |
| 137 | + |
| 138 | + defp platform_name({:win32, :nt}), do: "windows" |
| 139 | + defp platform_name({:unix, :darwin}), do: "macos" |
| 140 | + defp platform_name({:unix, _}), do: "linux" |
| 141 | + |
| 142 | + defp platform_arch({:win32, :nt}), do: "x86_64" |
| 143 | + defp platform_arch({:unix, :darwin}), do: "arm64" |
| 144 | + defp platform_arch({:unix, _}), do: "x86_64" |
| 145 | + |
| 146 | + def create_version_file(version) do |
| 147 | + content = """ |
| 148 | + ## AUTOGENERATED BY BUILD |
| 149 | + const BUILD_LABEL = "#{version}" |
| 150 | + const BUILD_DATE_STR = "#{:os.system_time(:seconds) |> DateTime.from_unix!() |> DateTime.to_iso8601()}" |
| 151 | + const BUILD_UNIX_TIME = #{:os.system_time(:seconds)} |
| 152 | + """ |
| 153 | + File.mkdir_p!("addons/vsk_version") |
| 154 | + File.write!("addons/vsk_version/build_constants.gd", content) |
| 155 | + end |
| 156 | + |
| 157 | + def export_template(_from_platform, _from_arch, target_platform, target_arch, version) do |
| 158 | + version = String.replace(version, ".custom_build", "") |
| 159 | + create_version_file(version) |
| 160 | + |
| 161 | + templatedir = case @os_family do |
| 162 | + {:win32, :nt} -> |
| 163 | + "#{System.get_env("USERPROFILE")}/AppData/Roaming/Godot/export_templates/#{version}/" |
| 164 | + {:unix, :darwin} -> |
| 165 | + "#{System.get_env("HOME")}/Library/Application Support/Godot/export_templates/#{version}/" |
| 166 | + {:unix, _} -> |
| 167 | + "#{System.get_env("HOME")}/.local/share/godot/export_templates/#{version}/" |
| 168 | + end |
| 169 | + File.mkdir_p!(templatedir) |
| 170 | + |
| 171 | + debug_file = case target_platform do |
| 172 | + "linuxbsd" -> "#{@extract_dir_extracted}/godot.linuxbsd.template_debug.double.#{target_arch}" |
| 173 | + "windows" -> "#{@extract_dir_extracted}/godot.windows.template_debug.double.#{target_arch}.llvm.exe" |
| 174 | + "web" -> "#{@extract_dir_extracted}/godot.web.template_debug.double.wasm32.dlink.zip" |
| 175 | + "macos" -> "#{@extract_dir_extracted}/godot_macos_double.zip" |
| 176 | + "android" -> "#{@extract_dir_extracted}/android_debug.apk" |
| 177 | + _ -> raise "Unsupported platform: #{target_platform}" |
| 178 | + end |
| 179 | + |
| 180 | + release_file = case target_platform do |
| 181 | + "linuxbsd" -> "#{@extract_dir_extracted}/godot.linuxbsd.template_release.double.#{target_arch}" |
| 182 | + "windows" -> "#{@extract_dir_extracted}/godot.windows.template_release.double.#{target_arch}.llvm.exe" |
| 183 | + "web" -> "#{@extract_dir_extracted}/godot.web.template_release.double.wasm32.dlink.zip" |
| 184 | + "macos" -> "#{@extract_dir_extracted}/godot_macos_double.zip" |
| 185 | + "android" -> "#{@extract_dir_extracted}/android_release.apk" |
| 186 | + _ -> raise "Unsupported platform: #{target_platform}" |
| 187 | + end |
| 188 | + |
| 189 | + debug_file_name = case target_platform do |
| 190 | + "linuxbsd" -> "linux_debug.#{target_arch}" |
| 191 | + "windows" -> "windows_debug_#{target_arch}.exe" |
| 192 | + "web" -> "web_dlink_nothreads_debug.zip" |
| 193 | + "macos" -> "macos_debug_#{target_arch}" |
| 194 | + "android" -> "android_debug.apk" |
| 195 | + _ -> "#{target_platform}_debug_#{target_arch}" |
| 196 | + end |
| 197 | + |
| 198 | + release_file_name = case target_platform do |
| 199 | + "linuxbsd" -> "linux_release.#{target_arch}" |
| 200 | + "windows" -> "windows_release_#{target_arch}.exe" |
| 201 | + "web" -> "web_dlink_nothreads_release.zip" |
| 202 | + "macos" -> "macos_release_#{target_arch}" |
| 203 | + "android" -> "android_release.apk" |
| 204 | + _ -> "#{target_platform}_release_#{target_arch}" |
| 205 | + end |
| 206 | + |
| 207 | + File.cp!(debug_file, "#{templatedir}/#{debug_file_name}", force: true) |
| 208 | + File.cp!(release_file, "#{templatedir}/#{release_file_name}", force: true) |
| 209 | + end |
| 210 | + |
| 211 | + def export_platform(from_platform, from_arch, target_platform, target_arch) do |
| 212 | + File.rm_rf!("#{@extract_dir}/export_#{target_platform}_#{target_arch}") |
| 213 | + File.mkdir_p!("#{@extract_dir}/export_#{target_platform}_#{target_arch}") |
| 214 | + |
| 215 | + editor_file = if from_platform == "windows" and File.exists?("#{@extract_dir_extracted}/godot.#{from_platform}.editor.double.#{from_arch}.llvm.exe") do |
| 216 | + "#{@extract_dir_extracted}/godot.#{from_platform}.editor.double.#{from_arch}.llvm.exe" |
| 217 | + else |
| 218 | + "#{@extract_dir_extracted}/godot.#{from_platform}.editor.double.#{from_arch}" |
| 219 | + end |
| 220 | + env = [{"ANDROID_SDK_ROOT", "#{File.cwd!()}/android_sdk"}, {"JAVA_HOME", "#{File.cwd!()}/jdk"}] |
| 221 | + arguments = ["--headless", "--path", ".", "--import"] |
| 222 | + System.cmd(editor_file, arguments, env: env) |
| 223 | + |
| 224 | + output_file = "#{@extract_dir}/export_#{target_platform}_#{target_arch}/#{@project_name}" |
| 225 | + output_file = if target_platform == "windows", do: output_file <> ".exe", else: output_file |
| 226 | + |
| 227 | + arguments = ["--headless", "--path", ".", "--export-release", target_platform, output_file] |
| 228 | + System.cmd(editor_file, arguments, env: env) |
| 229 | + IO.puts(Enum.join(arguments, " ")) |
| 230 | + |
| 231 | + case target_platform do |
| 232 | + "windows" -> |
| 233 | + System.cmd("strip", [output_file]) |
| 234 | + pdb_file = "#{@extract_dir_extracted}/godot.#{target_platform}.template_release.double.#{from_arch}.llvm.pdb" |
| 235 | + if File.exists?(pdb_file) do |
| 236 | + File.cp!(pdb_file, "#{@extract_dir}/export_#{target_platform}_#{target_arch}/#{Path.basename(pdb_file)}") |
| 237 | + else |
| 238 | + IO.puts("PDB file not found: #{pdb_file}") |
| 239 | + end |
| 240 | + _ -> :ok |
| 241 | + end |
| 242 | + end |
| 243 | + |
| 244 | + def upload_stage do |
| 245 | + upload_artifacts("windows", "x86_64") |
| 246 | + upload_artifacts("linux", "x86_64") |
| 247 | + end |
| 248 | + |
| 249 | + def upload_artifacts(platform, arch) do |
| 250 | + File.mkdir_p!("#{@extract_dir}/game") |
| 251 | + File.mkdir_p!("#{@extract_dir}/editor") |
| 252 | + File.cp_r!("#{@extract_dir_extracted}/export_#{platform}_#{arch}", "#{@extract_dir}/game") |
| 253 | + File.cp_r!("#{@extract_dir_extracted}/export_#{platform}_#{arch}/xr_grid_#{platform}_#{arch}", "#{@extract_dir}/editor/xr_grid_#{platform}_#{arch}_editor") |
| 254 | + end |
| 255 | +end |
0 commit comments