Class: Omnibus::Packager::MSI

Inherits:
WindowsBase show all
Defined in:
lib/omnibus/packagers/msi.rb

Constant Summary

Constants included from Util

Util::SHELLOUT_OPTIONS

Constants included from NullArgumentable

NullArgumentable::NULL

Instance Attribute Summary

Attributes inherited from Base

#project

DSL methods collapse

Instance Method Summary collapse

Methods inherited from WindowsBase

#algorithm, #cert_store_name, #certificate_subject, #is_signed?, #keypair_alias, #machine_store?, #sign_package, #signing_identity, #thumbprint, #timestamp_servers, #windows_package_version

Methods inherited from Base

build, #exclusions, id, #id, #initialize, #install_dir, #package_path, #resource_path, #resources_path, #run!, setup, #skip_packager, #staging_dir, #staging_dir_path

Methods included from Util

#compiler_safe_path, #copy_file, #create_directory, #create_file, #create_link, included, #path_key, #remove_directory, #remove_file, #retry_block, #shellout, #shellout!, #windows_safe_path

Methods included from Templating

included, #render_template, #render_template_content

Methods included from Sugarable

extended, included, #node

Methods included from NullArgumentable

included, #null?

Methods included from Logging

included

Methods included from Instrumentation

#measure

Methods included from Digestable

#digest, #digest_directory, included

Constructor Details

This class inherits a constructor from Omnibus::Packager::Base

Instance Method Details

#bundle_msi(val = false) ⇒ TrueClass, FalseClass

Signal that we’re building a bundle rather than a single package

Examples:

bundle_msi true

Parameters:

  • value (TrueClass, FalseClass)

    whether we’re a bundle or not

Returns:

  • (TrueClass, FalseClass)

    whether we’re a bundle or not



226
227
228
229
230
231
232
# File 'lib/omnibus/packagers/msi.rb', line 226

def bundle_msi(val = false)
  unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
    raise InvalidValue.new(:bundle_msi, "be TrueClass or FalseClass")
  end

  @bundle_msi ||= val
end

#bundle_nameObject



347
348
349
# File 'lib/omnibus/packagers/msi.rb', line 347

def bundle_name
  "#{project.package_name}-#{project.build_version}-#{project.build_iteration}-#{Config.windows_arch}.exe"
end

#candle_command(is_bundle: false) ⇒ String

Get the shell command to complie the project WIX files

Returns:

  • (String)


506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/omnibus/packagers/msi.rb', line 506

def candle_command(is_bundle: false)
  if is_bundle
    <<-EOH.split.join(" ").squeeze(" ").strip
    candle.exe
      -nologo
      #{wix_candle_flags}
      -ext WixBalExtension
      #{wix_extension_switches(wix_candle_extensions)}
      -dOmnibusCacheDir="#{windows_safe_path(File.expand_path(Config.cache_dir))}"
      "#{windows_safe_path(staging_dir, "bundle.wxs")}"
    EOH
  else
    <<-EOH.split.join(" ").squeeze(" ").strip
      candle.exe
        -nologo
        #{wix_candle_flags}
        #{wix_extension_switches(wix_candle_extensions)}
        -dProjectSourceDir="#{windows_safe_path(project.install_dir)}" "project-files.wxs"
        "#{windows_safe_path(staging_dir, "source.wxs")}"
    EOH
  end
end

#fast_msi(val = false) ⇒ TrueClass, FalseClass

Signal that we’re building a zip-based MSI

Examples:

fast_msi true

Parameters:

  • value (TrueClass, FalseClass)

    whether we’re building a zip-based MSI or not

Returns:

  • (TrueClass, FalseClass)

    whether we’re building a zip-based MSI or not



246
247
248
249
250
251
252
# File 'lib/omnibus/packagers/msi.rb', line 246

def fast_msi(val = false)
  unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
    raise InvalidValue.new(:fast_msi, "be TrueClass or FalseClass")
  end

  @fast_msi ||= val
end

#gem_path(glob = NULL) ⇒ String

Discovers a path to a gem/file included in a gem under the install directory.

Raises exception the glob matches 0 or more than 1 file/directory.

Examples:

gem_path 'chef-[0-9]*-mingw32' -> 'some/path/to/gems/chef-version-mingw32'

Parameters:

  • glob (String) (defaults to: NULL)

    a ruby acceptable glob path such as with **, *, [] etc.

Returns:

  • (String)

    path relative to the project’s install_dir



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/omnibus/packagers/msi.rb', line 291

def gem_path(glob = NULL)
  unless glob.is_a?(String) || null?(glob)
    raise InvalidValue.new(:glob, "be an String")
  end

  install_path = Pathname.new(project.install_dir)

  # Find path in which the Chef gem is installed
  search_pattern = install_path.join("**", "gems")
  search_pattern = search_pattern.join(glob) unless null?(glob)
  file_paths = Pathname.glob(search_pattern).find

  raise "Could not find `#{search_pattern}'!" if file_paths.none?
  raise "Multiple possible matches of `#{search_pattern}'! : #{file_paths}" if file_paths.count > 1

  file_paths.first.relative_path_from(install_path).to_s
end

#heat_commandString

Get the shell command to run heat in order to create a a WIX manifest of project files to be packaged into the MSI

Returns:

  • (String)


481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/omnibus/packagers/msi.rb', line 481

def heat_command
  if fast_msi
    <<-EOH.split.join(" ").squeeze(" ").strip
      heat.exe file "#{zip_name}.zip"
      -cg ProjectDir
      -dr INSTALLLOCATION
      -nologo -sfrag -srd -sreg -gg
      -out "project-files.wxs"
    EOH
  else
    <<-EOH.split.join(" ").squeeze(" ").strip
      heat.exe dir "#{windows_safe_path(project.install_dir)}"
        -nologo -srd -sreg -gg -cg ProjectDir
        -dr PROJECTLOCATION
        -var "var.ProjectSourceDir"
        -out "project-files.wxs"
    EOH
  end
end

#light_command(out_file, is_bundle: false) ⇒ String

Get the shell command to link the project WIX object files

Returns:

  • (String)


534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
# File 'lib/omnibus/packagers/msi.rb', line 534

def light_command(out_file, is_bundle: false)
  if is_bundle
    <<-EOH.split.join(" ").squeeze(" ").strip
    light.exe
      -nologo
      #{wix_light_delay_validation}
      -ext WixUIExtension
      -ext WixBalExtension
      #{wix_extension_switches(wix_light_extensions)}
      -cultures:#{localization}
      -loc "#{windows_safe_path(staging_dir, "localization-#{localization}.wxl")}"
      bundle.wixobj
      -out "#{out_file}"
    EOH
  else
    <<-EOH.split.join(" ").squeeze(" ").strip
      light.exe
        -nologo
        #{wix_light_delay_validation}
        -ext WixUIExtension
        #{wix_extension_switches(wix_light_extensions)}
        -cultures:#{localization}
        -loc "#{windows_safe_path(staging_dir, "localization-#{localization}.wxl")}"
        project-files.wixobj source.wixobj
        -out "#{out_file}"
    EOH
  end
end

#localization(val = "en-us") ⇒ String

Set or retrieve the localization. Take a look at this list of valid localizations.

Examples:

localization 'de-de'

Parameters:

  • val (String) (defaults to: "en-us")

    the localization to set

Returns:

  • (String)

    the set localization



269
270
271
272
273
274
275
# File 'lib/omnibus/packagers/msi.rb', line 269

def localization(val = "en-us")
  unless val.is_a?(String)
    raise InvalidValue.new(:localization, "be a String")
  end

  @localization ||= val
end

#msi_display_versionString

The display version calculated from the Omnibus::Project#build_version.

Returns:

  • (String)

See Also:



570
571
572
573
# File 'lib/omnibus/packagers/msi.rb', line 570

def msi_display_version
  versions = project.build_version.split(/[.+-]/)
  "#{versions[0]}.#{versions[1]}.#{versions[2]}"
end

#msi_nameObject



343
344
345
# File 'lib/omnibus/packagers/msi.rb', line 343

def msi_name
  "#{project.package_name}-#{project.build_version}-#{project.build_iteration}-#{Config.windows_arch}.msi"
end

#package_nameObject

See Also:



339
340
341
# File 'lib/omnibus/packagers/msi.rb', line 339

def package_name
  bundle_msi ? bundle_name : msi_name
end

#parameters(val = NULL) ⇒ Hash

Set or retrieve the custom msi building parameters.

Examples:

parameters {
  'MagicParam' => 'ABCD-1234'
}

Parameters:

  • val (Hash) (defaults to: NULL)

    the parameters to set

Returns:

  • (Hash)

    the set parameters



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/omnibus/packagers/msi.rb', line 135

def parameters(val = NULL)
  if null?(val)
    @parameters || {}
  else
    unless val.is_a?(Hash)
      raise InvalidValue.new(:parameters, "be a Hash")
    end

    @parameters = val
  end
end

#resources_dirString

The path where the MSI resources will live.

Returns:

  • (String)


356
357
358
# File 'lib/omnibus/packagers/msi.rb', line 356

def resources_dir
  File.expand_path("#{staging_dir}/Resources")
end

#upgrade_code(val = NULL) ⇒ Hash

Set or retrieve the upgrade code.

Examples:

upgrade_code 'ABCD-1234'

Parameters:

  • val (Hash) (defaults to: NULL)

    the UpgradeCode to set

Returns:

  • (Hash)

    the set UpgradeCode



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/omnibus/packagers/msi.rb', line 108

def upgrade_code(val = NULL)
  if null?(val)
    @upgrade_code || raise(MissingRequiredAttribute.new(self, :upgrade_code, "2CD7259C-776D-4DDB-A4C8-6E544E580AA1"))
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:upgrade_code, "be a String")
    end

    @upgrade_code = val
  end
end

#wix_candle_extension(extension) ⇒ Array

Set the wix candle extensions to load

Examples:

wix_candle_extension 'WixUtilExtension'

Parameters:

  • extension (String)

    A list of extensions to load

Returns:

  • (Array)

    The list of extensions that will be loaded



206
207
208
209
210
211
212
# File 'lib/omnibus/packagers/msi.rb', line 206

def wix_candle_extension(extension)
  unless extension.is_a?(String)
    raise InvalidValue.new(:wix_candle_extension, "be an String")
  end

  wix_candle_extensions << extension
end

#wix_candle_extensionsArray

Returns the extensions to use for candle

Returns:

  • (Array)

    the extensions that will be loaded for candle



591
592
593
# File 'lib/omnibus/packagers/msi.rb', line 591

def wix_candle_extensions
  @wix_candle_extensions ||= []
end

#wix_candle_flagsArray

Returns the options to use for candle

Returns:

  • (Array)

    the extensions that will be loaded for candle



601
602
603
604
# File 'lib/omnibus/packagers/msi.rb', line 601

def wix_candle_flags
  # we support x86 or x64.  No Itanium support (ia64).
  @wix_candle_flags ||= "-arch " + (Config.windows_arch.to_sym == :x86 ? "x86" : "x64")
end

#wix_extension_switches(arr) ⇒ String

Takes an array of wix extension names and creates a string that can be passed to wix to load those.

for example,

‘a’, ‘b’

> “-ext ‘a’ -ext ‘b’”

Returns:

  • (String)


615
616
617
# File 'lib/omnibus/packagers/msi.rb', line 615

def wix_extension_switches(arr)
  "#{arr.map { |e| "-ext '#{e}'" }.join(" ")}"
end

#wix_light_delay_validation(val = false) ⇒ String

Signal delay validation for wix light

Examples:

wix_light_deplay_validation true

Parameters:

  • value (TrueClass, FalseClass)

    whether to delay validation or not

Returns:

  • (String)

    whether we’re a bundle or not



180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/omnibus/packagers/msi.rb', line 180

def wix_light_delay_validation(val = false)
  unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
    raise InvalidValue.new(:iwix_light_delay_validation, "be TrueClass or FalseClass")
  end

  @delay_validation ||= val
  unless @delay_validation
    return ""
  end

  "-sval"
end

#wix_light_extension(extension) ⇒ Array

Set the wix light extensions to load

Examples:

wix_light_extension 'WixUtilExtension'

Parameters:

  • extension (String)

    A list of extensions to load

Returns:

  • (Array)

    The list of extensions that will be loaded



160
161
162
163
164
165
166
# File 'lib/omnibus/packagers/msi.rb', line 160

def wix_light_extension(extension)
  unless extension.is_a?(String)
    raise InvalidValue.new(:wix_light_extension, "be an String")
  end

  wix_light_extensions << extension
end

#wix_light_extensionsArray

Returns the extensions to use for light

Returns:

  • (Array)

    the extensions that will be loaded for light



581
582
583
# File 'lib/omnibus/packagers/msi.rb', line 581

def wix_light_extensions
  @wix_light_extensions ||= []
end

#write_bundle_filevoid

This method returns an undefined value.

Write the bundle file into the staging directory.



446
447
448
449
450
451
452
453
454
455
456
457
458
459
# File 'lib/omnibus/packagers/msi.rb', line 446

def write_bundle_file
  render_template(resource_path("bundle.wxs.erb"),
    destination: "#{staging_dir}/bundle.wxs",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
      upgrade_code: upgrade_code,
      parameters: parameters,
      version: windows_package_version,
      display_version: msi_display_version,
      msi: windows_safe_path(Config.package_dir, msi_name),
    })
end

#write_localization_filevoid

This method returns an undefined value.

Write the localization file into the staging directory.



365
366
367
368
369
370
371
372
373
# File 'lib/omnibus/packagers/msi.rb', line 365

def write_localization_file
  render_template(resource_path("localization-#{localization}.wxl.erb"),
    destination: "#{staging_dir}/localization-#{localization}.wxl",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
    })
end

#write_parameters_filevoid

This method returns an undefined value.

Write the parameters file into the staging directory.



380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/omnibus/packagers/msi.rb', line 380

def write_parameters_file
  render_template(resource_path("parameters.wxi.erb"),
    destination: "#{staging_dir}/parameters.wxi",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
      upgrade_code: upgrade_code,
      parameters: parameters,
      version: windows_package_version,
      display_version: msi_display_version,
    })
end

#write_source_filevoid

This method returns an undefined value.

Write the source file into the staging directory.



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/omnibus/packagers/msi.rb', line 399

def write_source_file
  paths = []

  # Remove C:/
  install_dir = project.install_dir.split("/")[1..-1].join("/")

  # Grab all parent paths
  Pathname.new(install_dir).ascend do |path|
    paths << path.to_s
  end

  # Create the hierarchy
  hierarchy = paths.reverse.inject({}) do |hash, path|
    hash[File.basename(path)] = path.gsub(/[^[:alnum:]]/, "").upcase + "LOCATION"
    hash
  end

  # The last item in the path MUST be named PROJECTLOCATION or else space
  # robots will cause permanent damage to you and your family.
  hierarchy[hierarchy.keys.last] = "PROJECTLOCATION"

  # If the path hierarchy is > 1, the customizable installation directory
  # should default to the second-to-last item in the hierarchy. If the
  # hierarchy is smaller than that, then just use the system drive.
  wix_install_dir = if hierarchy.size > 1
                      hierarchy.to_a[-2][1]
                    else
                      "WINDOWSVOLUME"
                    end

  render_template(resource_path("source.wxs.erb"),
    destination: "#{staging_dir}/source.wxs",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
      hierarchy: hierarchy,
      fastmsi: fast_msi,
      wix_install_dir: wix_install_dir,
    })
end

#zip_commandString

Get the shell command to create a zip file that contains the contents of the project install directory

Returns:

  • (String)


467
468
469
470
471
472
473
# File 'lib/omnibus/packagers/msi.rb', line 467

def zip_command
  <<-EOH.split.join(" ").squeeze(" ").strip
  7z a -r
  #{windows_safe_path(staging_dir)}\\#{zip_name}.zip
  #{windows_safe_path(project.install_dir)}\\*
  EOH
end

#zip_name(val = NULL) ⇒ TrueClass, FalseClass

Provide a custom zip name

Examples:

zip_name 'chef'

Parameters:

  • value (TrueClass, FalseClass)

    whether we’re building a zip-based MSI or not

Returns:

  • (TrueClass, FalseClass)

    whether we’re building a zip-based MSI or not



321
322
323
324
325
326
327
328
329
330
331
# File 'lib/omnibus/packagers/msi.rb', line 321

def zip_name(val = NULL)
  if null?(val)
    @zip_name || project.name
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:zip_name, "be an String")
    end

    @zip_name = val
  end
end