rubyxmltreegeneratormsitools

how to create a tree structure generator with ruby in a xml file?


I am on a project where I have to create an msi package from Ubuntu for Windows.

I managed to create an msi file from Ubuntu rgace to "msitools" which uses wxs files (it's like an xml file) to configure the package.

Example of an wxs file which take one file (FoobarAppl10.exe).

<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
  <Product Name='Foobar 1.0' Id='ABCDDCBA-86C7-4D14-AEC0-86416A69ABDE' UpgradeCode='ABCDDCBA-7349-453F-94F6-BCB5110BA4FD'
    Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='Acme Ltd.'>

<Package Id='*' Keywords='Installer' Description="Acme's Foobar 1.0 Installer"
  Comments='Foobar is a registered trademark of Acme Ltd.' Manufacturer='Acme Ltd.'
  InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />

<Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
<Property Id='DiskPrompt' Value="Acme's Foobar 1.0 Installation [1]" />

<Directory Id='TARGETDIR' Name='SourceDir'>
  <Directory Id='ProgramFilesFolder' Name='PFiles'>
    <Directory Id='Acme' Name='Acme'>
      <Directory Id='INSTALLDIR' Name='Foobar 1.0'>

        <Component Id='MainExecutable' Guid='ABCDDCBA-83F1-4F22-985B-FDB3C8ABD471'>
          <File Id='FoobarEXE' Name='FoobarAppl10.exe' DiskId='1' Source='FoobarAppl10.exe' KeyPath='yes'/>
        </Component>

      </Directory>
    </Directory>
  </Directory>
</Directory>

<Feature Id='Complete' Level='1'>
  <ComponentRef Id='MainExecutable' />
</Feature>
</Product>
</Wix>

Now my problem is to create a tree structure generator with ruby so that my wxs file packages all the files of my project without write it manually.

I write a code that can create a xml file and print my brute code in this:

folder

$ ls

build.wxs foobarAppl10.exe generator_xml.rb

require 'nokogiri'

out_file = File.new("generated.wxs", "w")

builder = Nokogiri::XML::Builder.new(:encoding => 'windows-1252') { |xml|
  xml.Wix('xmlns' => 'http://schemas.microsoft.com/wix/2006/wi') do
    xml.Product('Name' => 'Foobar 1.0', 'Id' => 'ABCDDCBA-86C7-4D14-AEC0-86416A69ABDE',
    'UpgradeCode' =>'ABCDDCBA-7349-453F-94F6-BCB5110BA4FD',  'Language' => '1033', 'Codepage' => '1252',
    'Version' => '1.0.0', 'Manufacturer' => 'Acme Ltd.') do
      
      xml.Package('Id' => '*', 'Keywords' => 'Installer', 'Description' => "Acme's Foobar 1.0 Installer", 
      'Comments' => 'Foobar is a registered trademark of Acme Ltd.', 'Manufacturer' => 'Acme Ltd.',
      'InstallerVersion' => '100', 'Languages' => '1033', 'Compressed' => 'yes', 'SummaryCodepage' => '1252')
    
      xml.Media('Id' => '1', 'Cabinet' => 'Sample.cab', 'EmbedCab' => 'yes', 'DiskPrompt' => "CD-ROM #1")
    
      xml.Property('Id' => 'DiskPromt', 'Value' => "Acme's Foobar 1.0 Installation [1]")

      xml.Directory('Id' => 'TARGETDIR', 'Name' =>'SourceDir') do
        xml.Directory('Id' => 'ProgramFilesFolder', 'Name' =>'PFiles') do
          xml.Directory('Id' => 'Acme', 'Name' =>'Acme') do
            xml.Directory('Id' => 'INSTALLDIR', 'Name' =>'Foobar 1.0') do

              xml.Component('Id' => 'MainExecutable', 'Guid' => 'ABCDDCBA-83F1-4F22-985B-FDB3C8ABD471') do
            
                xml.File('Id' => 'FoobarEXE', 'Name' => 'FoobarAppl10.exe', 'DiskId' => '1', 'Source' => 'FoobarAppl10.exe', 'KeyPath' =>'yes')
            
              end

            end
          end
        end
      end
      xml.Feature('Id' => 'Complete', 'Level' => '1') do
        xml.Component('Id' => 'MainExecutable')
      end
    end
  end
}

puts builder.to_xml

out_file.puts(builder.to_xml)
out_file.close

If someone know how to generate code from a tree structure it will help me a lot!


Solution

  • It sounds like you are want to create an XML structure that mimics a directory structure? Here is a recursive function that should do most of what you want, or at lease serve as a good staring point. The puts statements can be removed or replaced with any other actions that you need to perform when you find a directory or file.

    require 'nokogiri'
    
    def process_dir(current_path, xml)
        return if !File.directory?(current_path) || Dir.empty?(current_path)
        directory_name = current_path.split("/").last
        # insert code here to compute other attributes for Directory node
        xml.Directory(Name: directory_name) do
            Dir.children(current_path).each do |entry| 
                file = "#{current_path}/#{entry}"
                if !File.directory?(file)
                    # insert code here to compute other attributes for File node
                    xml.File(Name: entry)
                    puts "found file named #{entry} at #{current_path}"
                else
                    puts "found directory named #{current_path}/#{entry}"
                    process_dir("#{current_path}/#{entry}", xml)
                end
            end
        end
    end
    
    builder = Nokogiri::XML::Builder.new do |xml|
        xml.root { process_dir(".", xml) }
    end
    
    pp builder.to_xml