Top Level Namespace

Extended by:
Ruby2D::DSL
Includes:
Ruby2D

Defined Under Namespace

Modules: Ruby2D Classes: String

Constant Summary

Constants included from Ruby2D

Ruby2D::Colour, Ruby2D::VERSION

Instance Attribute Summary

Attributes included from Ruby2D

#height, #width

Instance Method Summary collapse

Methods included from Ruby2D::DSL

clear, close, get, off, on, render, set, show, update, window, window=

Methods included from Ruby2D

assets, gem_dir, test_media

Instance Method Details

#add_flags(type, flags) ⇒ Object

Add compiler and linker flags



19
20
21
22
23
24
25
26
# File 'ext/ruby2d/extconf.rb', line 19

def add_flags(type, flags)
  case type
  when :c
    $CFLAGS << " #{flags} "
  when :ld
    $LDFLAGS << " #{flags} "
  end
end

#add_ld_flags(ld_flags, name, type, dir = nil) ⇒ Object

Add linker flags



59
60
61
62
63
64
65
66
# File 'lib/ruby2d/cli/build.rb', line 59

def add_ld_flags(ld_flags, name, type, dir = nil)
  case type
  when :archive
    ld_flags << "#{dir}/lib#{name}.a "
  when :framework
    ld_flags << "-Wl,-framework,#{name} "
  end
end

#build(target, ruby2d_app) ⇒ Object

Build the user’s application



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/ruby2d/cli/build.rb', line 72

def build(target, ruby2d_app)

  # Check if source file provided is good
  if !ruby2d_app
    puts "Please provide a Ruby file to build"
    exit
  elsif !File.exist? ruby2d_app
    puts "Can't find file: #{ruby2d_app}"
    exit
  end

  # Add debugging information to produce backtrace
  if @debug then debug_flag = '-g' end

  # Create build directory
  FileUtils.mkdir_p 'build'

  # Assemble Ruby 2D library files into one '.rb' file

  ruby2d_lib_dir = "#{Ruby2D.gem_dir}/lib/ruby2d/"

  ruby2d_lib = ''
  @ruby2d_lib_files.each do |f|
    ruby2d_lib << File.read("#{ruby2d_lib_dir + f}.rb") + "\n\n"
  end

  File.write('build/ruby2d_lib.rb', ruby2d_lib)

  # Assemble the Ruby 2D C extension files into one '.c' file

  ruby2d_ext_dir = "#{Ruby2D.gem_dir}/ext/ruby2d/"

  ruby2d_ext = "#define MRUBY 1" << "\n\n"
  Dir["#{ruby2d_ext_dir}*.c"].each do |c_file|
    ruby2d_ext << File.read(c_file)
  end

  File.write('build/ruby2d_ext.c', ruby2d_ext)

  # Select `mrbc` executable based on platform
  case $RUBY2D_PLATFORM
  when :macos
    mrbc = "#{Ruby2D.assets}/macos/universal/bin/mrbc"
  when :windows
    mrbc = "#{Ruby2D.assets}/windows/mingw-w64-x86_64/bin/mrbc.exe"
  else
    mrbc = 'mrbc'
  end

  # Compile the Ruby 2D lib (`.rb` files) to mruby bytecode
  run_cmd "#{mrbc} #{debug_flag} -Bruby2d_lib -obuild/ruby2d_lib.c build/ruby2d_lib.rb"

  # Read the user's provided Ruby source file, copy to build dir and compile to bytecode
  File.open('build/ruby2d_app.rb', 'w') { |f| f << strip_require(ruby2d_app) }
  run_cmd "#{mrbc} #{debug_flag} -Bruby2d_app -obuild/ruby2d_app.c build/ruby2d_app.rb"

  # Combine contents of C source files and bytecode into one file
  open('build/app.c', 'w') do |f|
    ['ruby2d_app', 'ruby2d_lib', 'ruby2d_ext'].each do |c_file|
      f << File.read("build/#{c_file}.c") << "\n\n"
    end
  end

  # Compile the final application based on the target platform
  case target
  when :native
    compile_native
  when :web
    compile_web
  end

  # Remove files used in the build process
  clean_up unless @debug

end

#build_ios_tvos(rb_file, device) ⇒ Object

Build an iOS or tvOS app



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/ruby2d/cli/build.rb', line 350

def build_ios_tvos(rb_file, device)
  check_build_src_file(rb_file)

  # Check if MRuby exists; if not, quit
  if `which mruby`.empty?
    puts "#{'Error:'.error} Can't find MRuby, which is needed to build native Ruby 2D applications.\n"
    exit
  end

  # Add debugging information to produce backtrace
  if @debug then debug_flag = '-g' end

  # Assemble the Ruby 2D library in one `.rb` file and compile to bytecode
  make_lib
  `mrbc #{debug_flag} -Bruby2d_lib -obuild/lib.c build/lib.rb`

  # Read the provided Ruby source file, copy to build dir and compile to bytecode
  File.open('build/src.rb', 'w') { |file| file << strip_require(rb_file) }
  `mrbc #{debug_flag} -Bruby2d_app -obuild/src.c build/src.rb`

  # Copy over iOS project
  FileUtils.cp_r "#{@gem_dir}/assets/#{device}", "build"

  # Combine contents of C source files and bytecode into one file
  File.open("build/#{device}/main.c", 'w') do |f|
    f << "#define RUBY2D_IOS_TVOS 1" << "\n\n"
    f << "#define MRUBY 1" << "\n\n"
    f << File.read("build/lib.c") << "\n\n"
    f << File.read("build/src.c") << "\n\n"
    f << File.read("#{@gem_dir}/ext/ruby2d/ruby2d.c")
  end

  # TODO: Need add this functionality to the gem
  # Build the Xcode project
  `simple2d build --#{device} build/#{device}/MyApp.xcodeproj`

  # Clean up
  clean_up unless @debug

  # Success!
  puts "App created: `build/#{device}`"
end

#check_sdlObject

Check for SDL libraries



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'ext/ruby2d/extconf.rb', line 30

def check_sdl
  unless have_library('SDL2') && have_library('SDL2_image') && have_library('SDL2_mixer') && have_library('SDL2_ttf')

    $errors << "Couldn't find packages needed by Ruby 2D."

    case $platform
    when :linux, :linux_rpi
      # Fedora and CentOS
      if system('which yum')
        $errors << "Install the following packages using `yum` (or `dnf`) and try again:\n" <<
        "  SDL2-devel SDL2_image-devel SDL2_mixer-devel SDL2_ttf-devel".bold

      # Arch
      elsif system('which pacman')
        $errors << "Install the following packages using `pacman` and try again:\n" <<
        "  sdl2 sdl2_image sdl2_mixer sdl2_ttf".bold

      # openSUSE
      elsif system('which zypper')
        $errors << "Install the following packages using `zypper` and try again:\n" <<
        "  libSDL2-devel libSDL2_image-devel libSDL2_mixer-devel libSDL2_ttf-devel".bold

      # Ubuntu, Debian, and Mint
      # `apt` must be last because openSUSE has it aliased to `zypper`
      elsif system('which apt')
        $errors << "Install the following packages using `apt` and try again:\n" <<
        "  libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev".bold
      end
    when :bsd
      $errors << "Install the following packages using `pkg` and try again:\n" <<
      "  sdl2 sdl2_image sdl2_mixer sdl2_ttf".bold
    end

    $errors << "" << "See #{"ruby2d.com".bold} for additional help."
    print_errors; exit
  end
end

#clean_up(cmd = nil) ⇒ Object

Clean up unneeded build files



395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/ruby2d/cli/build.rb', line 395

def clean_up(cmd = nil)
  FileUtils.rm(
    Dir.glob('build/*.{rb,c,js}')
  )
  if cmd == :all
    puts "cleaning up..."
    FileUtils.rm_f 'build/app'
    FileUtils.rm_f 'build/app.js'
    FileUtils.rm_f 'build/app.html'
    FileUtils.rm_rf 'build/App.app'
    FileUtils.rm_rf 'build/ios'
    FileUtils.rm_rf 'build/tvos'
  end
end

#compile_nativeObject

Create a native executable using the available C compiler



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ruby2d/cli/build.rb', line 150

def compile_native

  # Get include directories
  incl_dir_ruby2d = "#{Ruby2D.gem_dir}/ext/ruby2d/"
  incl_dir_deps = "#{Ruby2D.assets}/include/"

  # Add compiler flags for each platform
  case $RUBY2D_PLATFORM

  when :macos
    ld_dir = "#{Ruby2D.assets}/macos/universal/lib"

    c_flags = '-arch arm64 -arch x86_64'

    ld_flags = ''
    ['mruby', 'SDL2', 'SDL2_image', 'SDL2_mixer', 'SDL2_ttf',
     'jpeg', 'jxl', 'avif', 'png', 'tiff', 'webp',
     'mpg123', 'ogg', 'FLAC', 'vorbis', 'vorbisfile', 'modplug',
     'freetype', 'harfbuzz', 'graphite2'].each do |name|
      add_ld_flags(ld_flags, name, :archive, ld_dir)
    end

    ld_flags << "-lz -lbz2 -liconv -lstdc++ "
    ['Cocoa', 'Carbon', 'CoreVideo', 'OpenGL', 'Metal', 'CoreAudio', 'AudioToolbox',
     'IOKit', 'GameController', 'ForceFeedback', 'CoreHaptics'].each do |name|
      add_ld_flags(ld_flags, name, :framework)
    end

  when :linux, :linux_rpi, :bsd
    # TODO: implement this
    # ld_flags = '-lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf -lm -lGL'

  when :windows

    if RUBY_PLATFORM =~ /ucrt/
      ld_dir = "#{Ruby2D.assets}/windows/mingw-w64-ucrt-x86_64/lib"
    else
      ld_dir = "#{Ruby2D.assets}/windows/mingw-w64-x86_64/lib"
    end

    ld_flags = '-static -Wl,--start-group '
    ['mruby',
     'SDL2',
     'SDL2_image', 'jpeg', 'png', 'tiff', 'webp', 'jxl', 'hwy', 'jbig', 'deflate', 'lzma', 'zstd', 'Lerc',
     'SDL2_mixer', 'mpg123', 'FLAC', 'vorbis', 'vorbisfile', 'ogg', 'modplug', 'opus', 'opusfile', 'sndfile',
     'SDL2_ttf', 'freetype', 'harfbuzz', 'graphite2', 'bz2', 'brotlicommon', 'brotlidec',
     'glew32', 'stdc++', 'z', 'ssp'
    ].each do |name|
      add_ld_flags(ld_flags, name, :archive, ld_dir)
    end
    ld_flags << '-lmingw32 -lopengl32 -lole32 -loleaut32 -limm32 -lversion -lwinmm -lrpcrt4 -mwindows -lsetupapi -ldwrite '\
                '-lws2_32 -lshlwapi '
    ld_flags << '-Wl,--end-group'
  end

  # Compile the app
  run_cmd "cc #{c_flags} -I#{incl_dir_ruby2d} -I#{incl_dir_deps} build/app.c #{ld_flags} -o build/app"

  create_macos_bundle if $RUBY2D_PLATFORM == :macos
end

#compile_webObject

Create a WebAssembly executable using Emscripten



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/ruby2d/cli/build.rb', line 213

def compile_web

  # Check for compiler toolchain issues
  if doctor_web(:building)
    puts "Fix errors before building.\n\n"
  end

  wasm_assets = "#{Ruby2D.assets}/wasm"

  # Get include directories
  incl_dir_ruby2d = "#{Ruby2D.gem_dir}/ext/ruby2d/"
  incl_dir_deps = "#{Ruby2D.assets}/include/"

  optimize_flags = '-Os --closure 1'
  ld_flags = "#{wasm_assets}/libmruby.a"

  # Compile using Emscripten
  run_cmd "emcc -s WASM=1 -I#{incl_dir_ruby2d} -I#{incl_dir_deps} "\
          "-s USE_SDL=2 -s USE_SDL_IMAGE=2 -s USE_SDL_MIXER=2 -s USE_SDL_TTF=2 "\
          "build/app.c #{ld_flags} -o build/app.html"

  # TODO: Copy HTML template from gem assets to build directory
  # FileUtils.cp "#{wasm_assets}/template.html", 'build/app.html'

  exit(1) unless $?.success?
end

#create_macos_bundleObject

Build an app bundle for macOS



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/ruby2d/cli/build.rb', line 307

def create_macos_bundle

  # Property list source for the bundle
  info_plist = %(
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleExecutable</key>
  <string>app</string>
  <key>CFBundleIconFile</key>
  <string>app.icns</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>CFBundleVersion</key>
  <string>1</string>
  <key>NSHighResolutionCapable</key>
  <string>True</string>
</dict>
</plist>
)

  # Create directories
  FileUtils.mkpath 'build/App.app/Contents/MacOS'
  FileUtils.mkpath 'build/App.app/Contents/Resources'

  # Create Info.plist and copy over assets
  File.open('build/App.app/Contents/Info.plist', 'w') { |f| f.write(info_plist) }
  FileUtils.cp 'build/app', 'build/App.app/Contents/MacOS/'
  # Consider using an icon:
  #   FileUtils.cp "#{@gem_dir}/assets/app.icns", 'build/App.app/Contents/Resources'

  # Clean up
  # FileUtils.rm_f 'build/app' unless @debug

  # Success!
  # puts 'macOS app bundle created: `build/App.app`'
end

#doctor_nativeObject



241
242
243
244
245
246
247
# File 'lib/ruby2d/cli/build.rb', line 241

def doctor_native
  # Check if MRuby exists; if not, quit
  if `which mruby`.empty?
    puts "#{'Error:'.error} Can't find `mruby`, which is needed to build native Ruby 2D applications.\n"
    exit
  end
end

#doctor_web(mode = nil) ⇒ Object

Check for problems with web build



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/ruby2d/cli/build.rb', line 251

def doctor_web(mode = nil)

  errors = false
  mruby_errors = false
  emscripten_errors = false

  puts "\nChecking for mruby"

  # Check for `mrbc`
  print '  mrbc...'
  if `which mrbc`.empty?
    puts 'not found'.error
    mruby_errors = true
  else
    puts 'found'.success
  end

  puts "\nChecking for Emscripten tools"

  # Check for `emcc`
  print '  emcc...'
  if `which emcc`.empty?
    puts 'not found'.error
    emscripten_errors = true
  else
    puts 'found'.success
  end

  # Check for `emar`
  print '  emar...'
  if `which emar`.empty?
    puts 'not found'.error
    emscripten_errors = true
  else
    puts 'found'.success
  end

  if mruby_errors || emscripten_errors then errors = true end

  if errors
    puts "\nErrors were found!\n\n"
    if mruby_errors
      puts "* Did you install mruby?"
    end
    if emscripten_errors
      puts "* Did you run \`./emsdk_env.sh\` ?", "  For help, check out the \"Getting Started\" guide on webassembly.org"
    end
    puts "\n"
    exit(1)
  else
    puts "\n👍 Everything looks good!\n\n"
  end
end

#launch_apple(device) ⇒ Object

Launch an iOS or tvOS app in a simulator



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ruby2d/cli/launch.rb', line 31

def launch_apple(device)
  case device
  when 'ios'
    if !File.exist? 'build/ios/build/Release-iphonesimulator/MyApp.app'
      puts "No iOS app built!"
      exit
    end
    puts `simple2d simulator --open "iPhone X" &&
          simple2d simulator --install "build/ios/build/Release-iphonesimulator/MyApp.app" &&
          simple2d simulator --launch "Ruby2D.MyApp"`
  when 'tvos'
    if !File.exist? 'build/tvos/build/Release-appletvsimulator/MyApp.app'
      puts "No tvOS app built!"
      exit
    end
    puts `simple2d simulator --open "Apple TV 4K" &&
          simple2d simulator --install "build/tvos/build/Release-appletvsimulator/MyApp.app" &&
          simple2d simulator --launch "Ruby2D.MyApp"`
  end
end

#launch_nativeObject

Launch a native app



4
5
6
7
8
9
10
# File 'lib/ruby2d/cli/launch.rb', line 4

def launch_native
  if !File.exist? 'build/app'
    puts "No native app built!"
    exit
  end
  `( cd build && ./app )`
end

#launch_webObject

Launch a web app



14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/ruby2d/cli/launch.rb', line 14

def launch_web
  if !File.exist? 'build/app.html'
    puts "No web app built!"
    exit
  end
  open_cmd = 'open'
  case RUBY_PLATFORM
  when /linux/
    open_cmd = "xdg-#{open_cmd}"
  when /mingw/
    open_cmd = "start"
  end
  system "#{open_cmd} build/app.html"
end

Print installation errors



10
11
12
13
14
15
# File 'ext/ruby2d/extconf.rb', line 10

def print_errors
  puts "
#{"== #{"Ruby 2D Installation Errors".error} =======================================\n"}
  #{$errors.join("\n  ")}\n
#{"======================================================================"}"
end

#run_cmd(cmd) ⇒ Object

Helpers ######################################################################



42
43
44
45
# File 'lib/ruby2d/cli/build.rb', line 42

def run_cmd(cmd)
  puts "#{'$'.info} #{cmd.bold}\n" if @debug
  system cmd
end

#set_linux_bsd_flagsObject

Set flags for Linux and BSD



79
80
81
82
83
84
# File 'ext/ruby2d/extconf.rb', line 79

def set_linux_bsd_flags
  check_sdl
  set_rpi_flags
  add_flags(:ld, "-lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf -lm")
  if $RUBY2D_PLATFORM == :linux then add_flags(:ld, '-lGL') end
end

#set_rpi_flagsObject

Set Raspberry Pi flags



70
71
72
73
74
75
# File 'ext/ruby2d/extconf.rb', line 70

def set_rpi_flags
  if $platform == :linux_rpi
    add_flags(:c, '-I/opt/vc/include')
    add_flags(:ld, '-L/opt/vc/lib -lbrcmGLESv2')
  end
end

#strip_require(file) ⇒ Object

Remove ‘require ’ruby2d’‘ from source file



49
50
51
52
53
54
55
# File 'lib/ruby2d/cli/build.rb', line 49

def strip_require(file)
  output = ''
  File.foreach(file) do |line|
    output << line unless line =~ /require ('|")ruby2d('|")/
  end
  return output
end

#use_usr_libsObject

Use SDL and other libraries installed by the user (not those bundled with the gem)



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'ext/ruby2d/extconf.rb', line 88

def use_usr_libs
  case $RUBY2D_PLATFORM
  when :macos
    add_flags(:c, `sdl2-config --cflags`)
    add_flags(:c, '-I/opt/homebrew/include')
    add_flags(:ld, `sdl2-config --libs`)
    add_flags(:ld, '-lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf')
    add_flags(:ld, '-Wl,-framework,OpenGL')
  when :windows
    add_flags(:ld, '-lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf')
    add_flags(:ld, '-lopengl32 -lglew32')
  when :linux_rpi
    set_linux_bsd_flags
  end
end