Module: ChocTop::Dmg

Included in:
Configuration
Defined in:
lib/choctop/dmg.rb

Instance Method Summary collapse

Instance Method Details

#add_eulaObject



189
190
191
192
193
194
195
196
# File 'lib/choctop/dmg.rb', line 189

def add_eula
  # TODO support EULA
  # hdiutil unflatten $@
	# /Developer/Tools/DeRez -useDF SLAResources.rsrc > build/temp/sla.r
	# /Developer/Tools/Rez -a build/temp/sla.r -o $@
	# hdiutil flatten $@
	
end

#add_file_to_dmg_src_folder(path, options) ⇒ Object



198
199
200
201
202
203
204
205
# File 'lib/choctop/dmg.rb', line 198

def add_file_to_dmg_src_folder(path, options)
  target = File.join(tmp_dmg_src_folder, options[:name])
  sh ::Escape.shell_command(['cp', '-r', path, target])
  if options[:exclude]
    exclude_list = options[:exclude].is_a?(Array) ? options[:exclude] : [options[:exclude].to_s]
    exclude_list.each { |exclude| sh ::Escape.shell_command(['rm', '-rf', File.join(target, exclude)]) }
  end
end


207
208
209
210
211
212
213
214
# File 'lib/choctop/dmg.rb', line 207

def add_link_to_dmg_src_folder(path, options)
  plist_name   = options[:name].gsub(/\.webloc$/, '')
  plist_target = File.join(tmp_dmg_src_folder, plist_name)
  target       = File.join(tmp_dmg_src_folder, options[:name])
  sh ::Escape.shell_command(['defaults', 'write', plist_target, 'URL', options[:url]])
  sh ::Escape.shell_command(['plutil', '-convert', 'xml1', '-o', target, "#{plist_target}.plist"])
  sh ::Escape.shell_command(['rm', "#{plist_target}.plist"])
end

#background_boundsObject



77
78
79
80
81
# File 'lib/choctop/dmg.rb', line 77

def background_bounds
  return [400, 300] unless background_file
  background = OSX::NSImage.alloc.initByReferencingFile(background_file).size.to_a
  [background.first, background.last + statusbar_height]
end

#configure_applications_iconObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/choctop/dmg.rb', line 155

def configure_applications_icon
  run_applescript <<-SCRIPT.gsub(/^      /, ''), "apps_icon_script"
    tell application "Finder"
      set applications_folder to displayed name of (path to applications folder) -- i18n
      set dest to disk "#{name}"
      set src to folder applications_folder of startup disk
      make new alias at dest to src
    end tell
  SCRIPT
  if applications_icon
    applications_path = "#{volume_path}/Applications"
    OSX::NSApplicationLoad()
    image = OSX::NSImage.alloc.initWithContentsOfFile(applications_icon)
    OSX::NSWorkspace.sharedWorkspace.setIcon_forFile_options(image, applications_path, nil)
  end
end

#configure_dmg_windowObject



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
# File 'lib/choctop/dmg.rb', line 92

def configure_dmg_window
  if background_file
    target_background = "#{volume_path}/#{volume_background}"
    FileUtils.mkdir_p(File.dirname(target_background))
    FileUtils.cp(background_file, target_background) 
  end
  script = <<-SCRIPT.gsub(/^      /, '')
    tell application "Finder"
       set applications_folder to displayed name of (path to applications folder) -- i18n
       set mountpoint to POSIX file ("#{volume_path}" as string) as alias
       tell folder mountpoint
           open
           tell container window
              set toolbar visible to false
              set statusbar visible to false -- doesn't do anything at DMG open time
              set current view to icon view
              delay 1 -- Sync
              set the bounds to {#{window_bounds.join(", ")}}
           end tell
           delay 1 -- Sync
           set icon size of the icon view options of container window to #{icon_size}
           set text size of the icon view options of container window to #{icon_text_size}
           set arrangement of the icon view options of container window to not arranged
           #{set_position_of_files}
           #{set_position_of_shortcuts}
           close
           open
           set the bounds of the container window to {#{window_bounds.join(", ")}}
           set background picture of the icon view options of container window to file "#{volume_background.gsub(/\//,':')}"
           update without registering applications
           delay 5 -- Sync
           close
       end tell
       -- Sync
       delay 5
    end tell
  SCRIPT
  puts "script: #{script}"
  run_applescript(script)
  sh "SetFile -a V '#{target_background}'" if background_file
end

#configure_volume_iconObject



85
86
87
88
89
90
# File 'lib/choctop/dmg.rb', line 85

def configure_volume_icon
  if volume_icon
    FileUtils.cp(volume_icon, "#{volume_path}/.VolumeIcon.icns")
    sh "SetFile -a C '#{volume_path}'"
  end
end

#convert_dmg_readonlyObject



183
184
185
186
187
# File 'lib/choctop/dmg.rb', line 183

def convert_dmg_readonly
  tmp_path = "/tmp/rw.dmg"
  FileUtils.mv(pkg, tmp_path)
  sh "hdiutil convert '#{tmp_path}' -format UDZO -imagekey zlib-level=9 -o '#{pkg}'"
end

#copy_filesObject

Two-phase copy: first to a tmp folder (to prevent recursion); then tmp folder to dmg_src_folder



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/choctop/dmg.rb', line 28

def copy_files
  FileUtils.mkdir_p(tmp_dmg_src_folder)
  files.each do |path, options|
    if options[:link]
      add_link_to_dmg_src_folder(path, options)
    else
      add_file_to_dmg_src_folder(path, options)
    end
  end
  FileUtils.rm_r(dmg_src_folder) if File.exists? dmg_src_folder
  FileUtils.mkdir_p(dmg_src_folder)
  Dir["#{tmp_dmg_src_folder}/*"].each { |f| FileUtils.cp_r(f, dmg_src_folder) }
end

#detach_dmgObject



172
173
174
175
176
177
178
179
180
181
# File 'lib/choctop/dmg.rb', line 172

def detach_dmg
  mounted_paths = `hdiutil info | grep '#{volume_path}' | grep "Apple_HFS"`.split("\n").map { |e| e.split(" ").first }
  mounted_paths.each do |path|
    begin
      sh "hdiutil detach '#{path}' -quiet -force"
    rescue StandardError => e
      p e
    end
  end
end

#include_applications_icon?Boolean

Returns:

  • (Boolean)


151
152
153
# File 'lib/choctop/dmg.rb', line 151

def include_applications_icon?
  target =~ /.app$/
end

#make_dmgObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/choctop/dmg.rb', line 42

def make_dmg
  prepare_files
  copy_files
  FileUtils.mkdir_p build_path
  FileUtils.mkdir_p mountpoint # TODO can we remove random mountpoints?
  FileUtils.rm_f(pkg) # make sure destination pkg doesn't already exist, or hdiutil will barf
  sh "hdiutil create -format UDRW -quiet -volname '#{name}' -srcfolder '#{dmg_src_folder}' '#{pkg}'"
  mount_dmg
  sh "bless --folder '#{volume_path}' --openfolder '#{volume_path}'"
  sh "sleep 1"

  puts "volume_icon: #{volume_icon.inspect}"
  puts "include_applications_icon?: #{include_applications_icon?.inspect}"
  configure_volume_icon
  configure_applications_icon if include_applications_icon?
  configure_dmg_window
end

#mount_dmgObject



60
61
62
# File 'lib/choctop/dmg.rb', line 60

def mount_dmg
  sh "hdiutil attach '#{pkg}' -mountpoint '#{volume_path}' -noautoopen -quiet"
end

#prepare_filesObject



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/choctop/dmg.rb', line 3

def prepare_files
  self.files = files.inject({}) do |files, file|
    path_or_helper, options = file
    path = case path_or_helper
      when Symbol
        send path_or_helper
      when Proc
        path_or_helper.call
      else
        path_or_helper
    end
    if path && File.exists?(path)
      files[path] = options 
      options[:name] ||= File.basename(path)
    end
    if path =~ %r{\.webloc$}
      files[path] = options 
      options[:name] ||= File.basename(path)
      options[:link] = true
    end
    files
  end
end

#run_applescript(applescript, tmp_file = "choctop-script") ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/choctop/dmg.rb', line 216

def run_applescript(applescript, tmp_file = "choctop-script")
  File.open(scriptfile = "/tmp/#{tmp_file}", "w") do |f|
    f << applescript
  end
  sh("osascript #{scriptfile}") do |ok, res|
    if ! ok
      p res
      puts volume_path
      exit 1
    end
  end
  applescript
end

#set_position_of_filesObject



134
135
136
137
138
139
140
141
# File 'lib/choctop/dmg.rb', line 134

def set_position_of_files
  files.map do |file_options|
    path, options = file_options
    target        = options[:name]
    position      = options[:position].join(", ")
    %Q{set position of item "#{target}" to {#{position}}}
  end.join("\n")
end

#set_position_of_shortcutsObject



143
144
145
146
147
148
149
# File 'lib/choctop/dmg.rb', line 143

def set_position_of_shortcuts
  if include_applications_icon?
    %Q{set position of item applications_folder to {#{applications_icon_position.join(", ")}}}
  else
    ""
  end
end

#statusbar_heightObject



83
# File 'lib/choctop/dmg.rb', line 83

def statusbar_height; 20; end

#tmp_dmg_src_folderObject



230
231
232
233
234
235
# File 'lib/choctop/dmg.rb', line 230

def tmp_dmg_src_folder
  @tmp_dmg_src_folder ||= begin
    require 'tmpdir'
    File.join(Dir.tmpdir, Time.now.to_i.to_s) # probably unique folder
  end
end

#volume_backgroundObject



64
65
66
# File 'lib/choctop/dmg.rb', line 64

def volume_background
  ".background/background#{File.extname(background_file)}"
end

#window_boundsObject



72
73
74
75
# File 'lib/choctop/dmg.rb', line 72

def window_bounds
  window_position + 
  window_position.zip(background_bounds).map { |w, b| w + b }
end

#window_positionObject



68
69
70
# File 'lib/choctop/dmg.rb', line 68

def window_position
  [50, 100]
end