reflex-packager 0.1.5 → 0.3.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/package.yml +310 -0
  3. data/.github/workflows/release-gem.yml +2 -40
  4. data/.github/workflows/tag.yml +11 -28
  5. data/.github/workflows/test.yml +16 -28
  6. data/.gitignore +2 -0
  7. data/ChangeLog.md +35 -0
  8. data/Gemfile.lock +26 -0
  9. data/README.md +232 -27
  10. data/Rakefile +21 -0
  11. data/VERSION +1 -1
  12. data/bin/reflex +2 -17
  13. data/examples/hello/icon.png +0 -0
  14. data/examples/hello/main.rb +18 -0
  15. data/examples/hello/reflex.yml +3 -0
  16. data/lib/reflex/packager/app_store_connect.rb +162 -0
  17. data/lib/reflex/packager/cli.rb +66 -26
  18. data/lib/reflex/packager/config.rb +239 -38
  19. data/lib/reflex/packager/data_compiler.rb +59 -0
  20. data/lib/reflex/packager/data_file.rb +183 -0
  21. data/lib/reflex/packager/data_loader.rb +127 -0
  22. data/lib/reflex/packager/gems.rb +282 -0
  23. data/lib/reflex/packager/library.rb +120 -0
  24. data/lib/reflex/packager/macos.rb +592 -92
  25. data/lib/reflex/packager/platform.rb +289 -10
  26. data/lib/reflex/packager/profile.rb +54 -17
  27. data/lib/reflex/packager/profiles/reflex.rb +43 -0
  28. data/lib/reflex/packager/rake.rb +115 -0
  29. data/lib/reflex/packager/stores/app_store.rb +60 -0
  30. data/lib/reflex/packager/stores/itchio.rb +53 -0
  31. data/lib/reflex/packager/templates/macos/AppStore.entitlements.erb +8 -0
  32. data/lib/reflex/packager/templates/macos/AppStoreExportOptions.plist.erb +14 -0
  33. data/lib/reflex/packager/templates/macos/ExportOptions.plist.erb +20 -0
  34. data/lib/reflex/packager/templates/macos/InfoPlist.strings.erb +5 -0
  35. data/lib/reflex/packager/templates/macos/boot.rb.erb +29 -0
  36. data/lib/reflex/packager/templates/macos/compile.rb.erb +7 -0
  37. data/lib/reflex/packager/templates/macos/main.mm.erb +10 -17
  38. data/lib/reflex/packager/templates/macos/project.yml.erb +60 -3
  39. data/lib/reflex/packager/templates/windows/app.manifest.erb +25 -0
  40. data/lib/reflex/packager/templates/windows/app.rc.erb +38 -0
  41. data/lib/reflex/packager/templates/windows/boot.rb.erb +32 -0
  42. data/lib/reflex/packager/templates/windows/main.cpp.erb +47 -0
  43. data/lib/reflex/packager/templates/windows/runtime.manifest.erb +7 -0
  44. data/lib/reflex/packager/windows.rb +370 -0
  45. data/lib/reflex/packager.rb +18 -1
  46. data/reflex-packager.gemspec +8 -7
  47. data/test/helper.rb +54 -7
  48. data/test/test_packager_app_store_connect.rb +108 -0
  49. data/test/test_packager_cli.rb +40 -5
  50. data/test/test_packager_config.rb +166 -27
  51. data/test/test_packager_data_file.rb +96 -0
  52. data/test/test_packager_data_loader.rb +114 -0
  53. data/test/test_packager_library.rb +81 -0
  54. data/test/test_packager_macos.rb +714 -87
  55. data/test/test_packager_platform.rb +16 -0
  56. data/test/test_packager_rake.rb +165 -0
  57. data/test/test_packager_stores_app_store.rb +66 -0
  58. data/test/test_packager_stores_itchio.rb +38 -0
  59. data/test/test_packager_windows.rb +685 -0
  60. metadata +72 -15
  61. data/.github/workflows/utils.rb +0 -127
  62. data/lib/reflex/packager/templates/macos/Podfile.erb +0 -30
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.security.app-sandbox</key>
6
+ <true/>
7
+ </dict>
8
+ </plist>
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>method</key>
6
+ <string>app-store-connect</string>
7
+ <key>signingStyle</key>
8
+ <string>automatic</string>
9
+ <key>teamID</key>
10
+ <string><%= config.macos.codesign_team_id %></string>
11
+ <key>destination</key>
12
+ <string>export</string>
13
+ </dict>
14
+ </plist>
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>method</key>
6
+ <string>developer-id</string>
7
+ <key>signingStyle</key>
8
+ <%- if cloud_signing? -%>
9
+ <string>automatic</string>
10
+ <%- else -%>
11
+ <string>manual</string>
12
+ <key>signingCertificate</key>
13
+ <string><%= codesign_identity.encode xml: :text %></string>
14
+ <%- end -%>
15
+ <key>teamID</key>
16
+ <string><%= config.macos.codesign_team_id %></string>
17
+ <key>destination</key>
18
+ <string><%= notarize.include?(:submit) ? 'upload' : 'export' %></string>
19
+ </dict>
20
+ </plist>
@@ -0,0 +1,5 @@
1
+ "CFBundleName" = <%= name.to_json %>;
2
+ "CFBundleDisplayName" = <%= name.to_json %>;
3
+ <%- if copyright -%>
4
+ "NSHumanReadableCopyright" = <%= copyright.to_json %>;
5
+ <%- end -%>
@@ -0,0 +1,29 @@
1
+ begin
2
+ Dir.chdir File.join(__dir__, 'app')
3
+ $LOAD_PATH.unshift Dir.pwd
4
+ <%- if pack? -%>
5
+ require 'reflex/packager/data_loader'
6
+ Reflex::Packager::DataLoader.setup Dir.pwd
7
+
8
+ <%- end -%>
9
+ load File.join(Dir.pwd, <%= start_script.dump %>)
10
+ rescue SystemExit, SignalException
11
+ raise
12
+ rescue Exception => e
13
+ if $stderr.tty?
14
+ $stderr.puts e.full_message
15
+ else
16
+ begin
17
+ require 'reflex'
18
+ dirs = Regexp.union [File.dirname(__FILE__), __dir__].map {"#{_1}/"}
19
+ message = e.full_message(highlight: false).lines
20
+ .reject {_1.match? %r{^\s+from (eval|#{Regexp.escape __FILE__}|(#{dirs})reflex\.bundle/Contents/Resources/lib/reflex/packager/data_loader\.rb):}}
21
+ .join
22
+ .gsub(dirs, '')
23
+ Reflex.alert message, title: <%= config.name.dump %>
24
+ rescue Exception => alert_error
25
+ $stderr.puts "failed to alert: #{alert_error.message} (#{alert_error.class})"
26
+ end
27
+ end
28
+ raise
29
+ end
@@ -0,0 +1,7 @@
1
+ begin
2
+ require <%= compiler.dump %>
3
+ Reflex::Packager::DataCompiler.write <%= app_dir.dump %>, <%= path.dump %>
4
+ rescue Exception => e
5
+ $stderr.puts e.full_message
6
+ exit 1
7
+ end
@@ -1,6 +1,7 @@
1
1
  // -*- mode: objc -*-
2
2
  #import <Cocoa/Cocoa.h>
3
3
  #import <CRuby.h>
4
+ #include <locale.h>
4
5
 
5
6
 
6
7
  extern "C"
@@ -14,30 +15,22 @@ extern "C"
14
15
  int
15
16
  main (int argc, const char* argv[])
16
17
  {
18
+ // for non-ASCII paths, before initializing CRuby
19
+ setlocale(LC_CTYPE, "UTF-8");
20
+
17
21
  @autoreleasepool
18
22
  {
19
- NSString* appDir =
20
- [NSBundle.mainBundle.resourcePath stringByAppendingPathComponent:@"app"];
21
-
22
- // the application loads images by paths relative to the script
23
- [NSFileManager.defaultManager changeCurrentDirectoryPath:appDir];
24
-
25
23
  <%- extensions.each do |ext| -%>
26
24
  [CRuby addExtension:@"<%= ext %>" init:^{Init_<%= ext %>();}];
27
25
  <%- end -%>
28
26
 
29
- for (NSString* lib in @[<%= libraries.map {|l| "@\"#{l}\""}.join ', ' %>])
27
+ for (NSString* lib in @[<%= lib_names.reverse.map {"@\"#{_1}\""}.join ', ' %>])
30
28
  [CRuby addLibrary:lib bundle:NSBundle.mainBundle];
31
29
 
32
- NSString* main = [appDir stringByAppendingPathComponent:@"<%= config.main %>"];
33
-
34
- // add the script directory to the load path so that apps split
35
- // into multiple files can require each other
36
- [CRuby evaluate:[NSString stringWithFormat:@"$LOAD_PATH.unshift '%@'", appDir]];
37
-
38
- // the script calls Reflex.start, which runs [NSApp run] and blocks
39
- // here until the application quits
40
- [CRuby start:main];
30
+ NSString* boot = [NSBundle.mainBundle.resourcePath stringByAppendingPathComponent:@"boot.rb"];
31
+ return [CRuby start:boot rescue:^(CRBValue* exception) {
32
+ CRBValue* message = [exception call:@"full_message" rescue:^(CRBValue* e) {}];
33
+ NSLog(@"%@", (message ?: [exception call:@"class"]).toString);
34
+ }];
41
35
  }
42
- return 0;
43
36
  }
@@ -7,13 +7,28 @@ options:
7
7
  settings:
8
8
  base:
9
9
  PRODUCT_BUNDLE_IDENTIFIER: <%= config.bundle_id %>
10
- MARKETING_VERSION: "<%= config.version %>"
10
+ PRODUCT_NAME: <%= app_name.to_json %>
11
+ MARKETING_VERSION: <%= config.display_version.to_json %>
12
+ CURRENT_PROJECT_VERSION: <%= config.build_version.to_json %>
11
13
  ARCHS: <%= config.macos.archs.join ' ' %>
12
14
  ONLY_ACTIVE_ARCH: NO
13
- CODE_SIGN_IDENTITY: <%= config.macos.codesign_identity.inspect %>
15
+ <%- if signing_to_distribute? # Developer ID signs the app exported, not the archive -%>
16
+ CODE_SIGN_IDENTITY: "-"
17
+ <%- else -%>
18
+ CODE_SIGN_IDENTITY: <%= codesign_identity.inspect %>
19
+ <%- end -%>
14
20
  <%- if config.macos.codesign_team_id -%>
15
21
  DEVELOPMENT_TEAM: <%= config.macos.codesign_team_id %>
22
+ ENABLE_HARDENED_RUNTIME: YES
23
+ <%- end -%>
24
+ CLANG_CXX_LANGUAGE_STANDARD: c++20
25
+ CLANG_ENABLE_OBJC_ARC: NO
26
+ <%- unless verbose? -%>
27
+ GCC_WARN_INHIBIT_ALL_WARNINGS: YES
16
28
  <%- end -%>
29
+ HEADER_SEARCH_PATHS: <%= header_search_paths.to_json %>
30
+ SYSTEM_HEADER_SEARCH_PATHS: <%= system_header_search_paths.to_json %>
31
+ OTHER_LDFLAGS: [-lz, -lffi]
17
32
 
18
33
  targets:
19
34
  <%= target %>:
@@ -24,17 +39,59 @@ targets:
24
39
  - path: app
25
40
  type: folder
26
41
  buildPhase: resources
42
+ - path: boot.rb
43
+ buildPhase: resources
44
+ <%- Dir.glob('*.bundle', base: File.join(build_dir, 'Bundles')).sort.each do |bundle| -%>
45
+ - path: Bundles/<%= bundle %>
46
+ buildPhase: resources
47
+ <%- end -%>
27
48
  <%- if config.icon -%>
28
49
  - path: AppIcon.icns
29
50
  buildPhase: resources
51
+ <%- end -%>
52
+ - path: <%= File.join(cruby_dir, 'src').to_json %>
53
+ name: CRuby
54
+ <%- build_infos.each do |lib, info| -%>
55
+ <%- root = lib.root -%>
56
+ <%- flags = info[:defs].map {"-D#{_1}"}.join ' ' -%>
57
+ <%- unless info[:srcs].empty? -%>
58
+ - path: <%= root.to_json %>
59
+ name: <%= lib %>
60
+ includes: <%= info[:srcs].map {_1.delete_prefix "#{root}/"}.to_json %>
61
+ compilerFlags: <%= flags.to_json %>
62
+ <%- end -%>
63
+ <%- unless info[:vendor_srcs].empty? -%>
64
+ - path: <%= File.join(root, 'vendor').to_json %>
65
+ name: <%= lib %>-vendor
66
+ includes: <%= info[:vendor_srcs].map {_1.delete_prefix "#{root}/vendor/"}.to_json %>
67
+ compilerFlags: <%= "#{flags} -w".to_json %>
68
+ <%- end -%>
69
+ <%- end -%>
70
+ dependencies:
71
+ - framework: <%= File.join(cruby_dir, 'CRuby', 'CRuby.xcframework').to_json %>
72
+ embed: false
73
+ <%- frameworks.each do |framework| -%>
74
+ - sdk: <%= framework %>.framework
30
75
  <%- end -%>
31
76
  info:
32
77
  path: src/Info.plist
33
78
  properties:
34
79
  CFBundleName: <%= config.name.inspect %>
35
80
  CFBundleDisplayName: <%= config.name.inspect %>
36
- CFBundleShortVersionString: "<%= config.version %>"
81
+ CFBundleShortVersionString: <%= config.display_version.to_json %>
82
+ CFBundleVersion: <%= config.build_version.to_json %>
83
+ <%- if config.copyright -%>
84
+ NSHumanReadableCopyright: <%= config.copyright.to_json %>
85
+ <%- end -%>
37
86
  NSHighResolutionCapable: true
87
+ <%- if config.macos.category -%>
88
+ LSApplicationCategoryType: <%= config.macos.category %>
89
+ <%- end -%>
90
+ # no encryption but the system's, not to be asked by App Store Connect
91
+ ITSAppUsesNonExemptEncryption: false
92
+ <%- if config.localizations.size > 1 -%>
93
+ LSHasLocalizedDisplayName: true
94
+ <%- end -%>
38
95
  <%- if config.icon -%>
39
96
  CFBundleIconFile: AppIcon
40
97
  <%- end -%>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3
+ <assemblyIdentity type="win32" name="<%= target %>" version="<%= product_version %>"/>
4
+
5
+ <!-- the dlls in lib/, a private assembly listed in lib/lib.manifest -->
6
+ <dependency>
7
+ <dependentAssembly>
8
+ <assemblyIdentity type="win32" name="<%= Windows::RUNTIME_DIR %>" version="<%= Windows::RUNTIME_VERSION %>"/>
9
+ </dependentAssembly>
10
+ </dependency>
11
+
12
+ <!-- Windows 10 and 11 -->
13
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
14
+ <application>
15
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
16
+ </application>
17
+ </compatibility>
18
+
19
+ <!-- paths and file names in UTF-8 whatever the system locale is -->
20
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
21
+ <windowsSettings>
22
+ <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
23
+ </windowsSettings>
24
+ </application>
25
+ </assembly>
@@ -0,0 +1,38 @@
1
+ // application manifest (RT_MANIFEST)
2
+ 1 24 "app.manifest"
3
+ <%- if config.icon -%>
4
+
5
+ // icon of the executable, and of its windows through this id
6
+ 1 ICON "app.ico"
7
+ <%- end -%>
8
+
9
+ // shown in the properties of the executable, and as the name of the
10
+ // process in the task manager
11
+ 1 VERSIONINFO
12
+ FILEVERSION <%= file_version .tr '.', ',' %>
13
+ PRODUCTVERSION <%= product_version.tr '.', ',' %>
14
+ FILEOS 0x40004// VOS_NT_WINDOWS32
15
+ FILETYPE 0x1// VFT_APP
16
+ BEGIN
17
+ BLOCK "StringFileInfo"
18
+ BEGIN
19
+ <%- config.localizations.each do |lang, values| -%>
20
+ BLOCK "<%= "%04X04B0" % langid(lang) %>"// <%= lang %>, Unicode
21
+ BEGIN
22
+ VALUE "FileDescription", <%= rc_string values[:name] %>
23
+ VALUE "FileVersion", <%= rc_string config.build_version %>
24
+ VALUE "InternalName", <%= rc_string target %>
25
+ <%- if values[:copyright] -%>
26
+ VALUE "LegalCopyright", <%= rc_string values[:copyright] %>
27
+ <%- end -%>
28
+ VALUE "OriginalFilename", <%= rc_string "#{target}.exe" %>
29
+ VALUE "ProductName", <%= rc_string values[:name] %>
30
+ VALUE "ProductVersion", <%= rc_string config.display_version %>
31
+ END
32
+ <%- end -%>
33
+ END
34
+ BLOCK "VarFileInfo"
35
+ BEGIN
36
+ VALUE "Translation", <%= config.localizations.keys.map {"0x%x, 1200" % langid(_1)}.join ", " %>
37
+ END
38
+ END
@@ -0,0 +1,32 @@
1
+ %w[<%= load_dirs.join ' ' %>].reverse_each do |dir|
2
+ $LOAD_PATH.unshift File.join(__dir__, dir, 'lib')
3
+ end
4
+
5
+ Dir.chdir File.join(__dir__, 'app')
6
+ $LOAD_PATH.unshift Dir.pwd
7
+
8
+ begin
9
+ <%- if pack? -%>
10
+ require 'reflex/packager/data_loader'
11
+ Reflex::Packager::DataLoader.setup Dir.pwd
12
+
13
+ <%- end -%>
14
+ load File.join(Dir.pwd, <%= start_script.dump %>)
15
+ <%- unless config.windows.console? -%>
16
+ rescue SystemExit, SignalException
17
+ raise
18
+ rescue Exception => e
19
+ begin
20
+ require 'reflex'
21
+ dirs = Regexp.union [File.dirname(__FILE__), __dir__].map {"#{_1}/"}
22
+ message = e.full_message(highlight: false).lines
23
+ .reject {_1.match? %r{^\s+from (eval|#{Regexp.escape __FILE__}|(#{dirs})reflex/lib/reflex/packager/data_loader\.rb):}}
24
+ .join
25
+ .gsub(dirs, '')
26
+ Reflex.alert message, title: <%= config.name.dump %>
27
+ rescue Exception => alert_error
28
+ $stderr.puts "failed to alert: #{alert_error.message} (#{alert_error.class})"
29
+ end
30
+ raise
31
+ <%- end -%>
32
+ end
@@ -0,0 +1,47 @@
1
+ // -*- mode: c++ -*-
2
+ #include <ruby.h>
3
+ #include <windows.h>
4
+ #include <string>
5
+ #include <vector>
6
+
7
+
8
+ extern "C"
9
+ {
10
+ void ruby_init_ext (const char* name, void (*init)(void));
11
+ <%- extensions.each do |ext| -%>
12
+ void Init_<%= ext %> ();
13
+ <%- end -%>
14
+ }
15
+
16
+
17
+ static std::string
18
+ boot_script ()
19
+ {
20
+ std::wstring path(32768, L'\0');
21
+ path.resize(GetModuleFileNameW(NULL, &path[0], (DWORD) path.size()));
22
+ path.erase(path.find_last_of(L"\\/") + 1);
23
+ path += L"lib\\boot.rb";
24
+
25
+ int size = WideCharToMultiByte(CP_UTF8, 0, path.c_str(), -1, NULL, 0, NULL, NULL);
26
+ std::string utf8(size, '\0');
27
+ WideCharToMultiByte(CP_UTF8, 0, path.c_str(), -1, &utf8[0], size, NULL, NULL);
28
+ utf8.resize(size - 1);
29
+ return utf8;
30
+ }
31
+
32
+ int
33
+ main (int argc, char** argv)
34
+ {
35
+ ruby_sysinit(&argc, &argv);
36
+ RUBY_INIT_STACK;
37
+ ruby_init();
38
+
39
+ <%- extensions.each do |ext| -%>
40
+ ruby_init_ext("<%= ext %>.so", Init_<%= ext %>);
41
+ <%- end -%>
42
+
43
+ std::string boot = boot_script();
44
+ std::vector<char*> args = {argv[0], &boot[0]};
45
+ args.insert(args.end(), argv + 1, argv + argc);
46
+ return ruby_run_node(ruby_options((int) args.size(), args.data()));
47
+ }
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3
+ <assemblyIdentity type="win32" name="<%= Windows::RUNTIME_DIR %>" version="<%= Windows::RUNTIME_VERSION %>"/>
4
+ <%- dlls.each do |dll| -%>
5
+ <file name="<%= dll %>"/>
6
+ <%- end -%>
7
+ </assembly>