1 # -*- coding: utf-8 -*-
5 # Tails: The Amnesic Incognito Live System
6 # Copyright © 2012 Tails developers <tails@boum.org>
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 $:.unshift File.expand_path('../vagrant/lib', __FILE__)
27 require 'tails_build_settings'
28 require 'vagrant_version'
30 # Path to the directory which holds our Vagrantfile
31 VAGRANT_PATH = File.expand_path('../vagrant', __FILE__)
33 # Branches that are considered 'stable' (used to select SquashFS compression)
34 STABLE_BRANCH_NAMES = ['stable', 'testing']
36 # Environment variables that will be exported to the build script
37 EXPORTED_VARIABLES = ['http_proxy', 'MKSQUASHFS_OPTIONS', 'TAILS_RAM_BUILD', 'TAILS_CLEAN_BUILD', 'TAILS_BOOTSTRAP_CACHE']
39 # Let's save the http_proxy set before playing with it
40 EXTERNAL_HTTP_PROXY = ENV['http_proxy']
43 INTERNEL_HTTP_PROXY = "http://#{VIRTUAL_MACHINE_HOSTNAME}:3142"
46 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
50 name = env.primary_machine_name
51 return env.machine(name, env.default_provider)
57 return primary_vm.state
59 return primary_vm.state.id
66 info = vm.driver.execute 'showvminfo', uuid, '--machinereadable'
67 $1.to_i if info =~ /^memory=(\d+)/
73 info = vm.driver.execute 'showvminfo', uuid, '--machinereadable'
74 $1.to_i if info =~ /^cpus=(\d+)/
78 primary_vm_state == :running
81 def enough_free_memory?
82 return false unless RbConfig::CONFIG['host_os'] =~ /linux/i
85 usable_free_mem = `free`.split[16].to_i
86 usable_free_mem > VM_MEMORY_FOR_RAM_BUILDS * 1024
93 branch_name = `git name-rev --name-only HEAD`
94 tag_name = `git describe --exact-match HEAD 2> /dev/null`
95 STABLE_BRANCH_NAMES.include? branch_name.chomp or tag_name.chomp.length > 0
99 return nil unless RbConfig::CONFIG['host_os'] =~ /linux/i
102 File.read('/proc/cpuinfo').scan(/^processor\s+:/).count
108 task :parse_build_options do
111 # Default to in-memory builds if there is enough RAM available
112 options += 'ram ' if enough_free_memory?
114 # Use in-VM proxy unless an external proxy is set
115 options += 'vmproxy ' unless EXTERNAL_HTTP_PROXY
117 # Default to fast compression on development branches
118 options += 'gzipcomp ' unless is_release?
120 # Make sure release builds are clean
121 options += 'cleanall ' if is_release?
123 # Default to the number of system CPUs when we can figure it out
125 options += "cpus=#{cpus} " if cpus
127 options += ENV['TAILS_BUILD_OPTIONS'] if ENV['TAILS_BUILD_OPTIONS']
128 options.split(' ').each do |opt|
130 # Memory build settings
132 unless vm_running? || enough_free_memory?
133 abort "Not enough free memory to do an in-memory build. Aborting."
135 ENV['TAILS_RAM_BUILD'] = '1'
137 ENV['TAILS_RAM_BUILD'] = nil
138 # Bootstrap cache settings
140 ENV['TAILS_BOOTSTRAP_CACHE'] = '1'
142 ENV['TAILS_BOOTSTRAP_CACHE'] = nil
143 # HTTP proxy settings
145 abort "No HTTP proxy set, but one is required by TAILS_BUILD_OPTIONS. Aborting." unless EXTERNAL_HTTP_PROXY
146 ENV['http_proxy'] = EXTERNAL_HTTP_PROXY
148 ENV['http_proxy'] = INTERNEL_HTTP_PROXY
150 ENV['http_proxy'] = nil
151 # SquashFS compression settings
153 ENV['MKSQUASHFS_OPTIONS'] = '-comp gzip'
155 ENV['MKSQUASHFS_OPTIONS'] = nil
158 ENV['TAILS_CLEAN_BUILD'] = '1'
159 # Virtual CPUs settings
161 ENV['TAILS_BUILD_CPUS'] = $1
164 ENV['TAILS_BUILD_IGNORE_CHANGES'] = '1'
169 task :ensure_clean_repository do
170 unless `git status --porcelain`.empty?
171 if ENV['TAILS_BUILD_IGNORE_CHANGES']
172 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
174 You have uncommited changes in the Git repository. They will
175 be ignored for the upcoming build.
179 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
181 You have uncommited changes in the Git repository. Due to limitations
182 of the build system, you need to commit them before building Tails.
184 If you don't care about those changes and want to build Tails nonetheless,
185 please add `ignorechanges` to the TAILS_BUILD_OPTIONS environment
189 abort 'Uncommited changes. Aborting.'
194 task :validate_http_proxy do
196 proxy_host = URI.parse(ENV['http_proxy']).host
199 ENV['http_proxy'] = nil
200 $stderr.puts "Ignoring invalid HTTP proxy."
204 if ['localhost', '[::1]'].include?(proxy_host) || proxy_host.start_with?('127.0.0.')
205 abort 'Using an HTTP proxy listening on the loopback is doomed to fail. Aborting.'
208 $stderr.puts "Using HTTP proxy: #{ENV['http_proxy']}"
210 $stderr.puts "No HTTP proxy set."
215 task :build => ['parse_build_options', 'ensure_clean_repository', 'validate_http_proxy', 'vm:up'] do
216 exported_env = EXPORTED_VARIABLES.select { |k| ENV[k] }.
217 collect { |k| "#{k}='#{ENV[k]}'" }.join(' ')
219 chan = primary_vm.channel
221 chan = primary_vm.communicate
223 status = chan.execute("#{exported_env} build-tails",
224 :error_check => false) do |fd, data|
225 (fd == :stdout ? $stdout : $stderr).write data
228 # Move build products to the current directory
229 FileUtils.mv Dir.glob("#{VAGRANT_PATH}/tails-*"),
230 File.expand_path('..', __FILE__), :force => true
236 desc 'Start the build virtual machine'
237 task :up => ['parse_build_options', 'validate_http_proxy'] do
238 case primary_vm_state
240 # Do not use non-existant in-VM proxy to download the basebox
241 if ENV['http_proxy'] == INTERNEL_HTTP_PROXY
242 ENV['http_proxy'] = nil
243 restore_internal_proxy = true
246 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
248 This is the first time that the Tails builder virtual machine is
249 started. The virtual machine template is about 300 MB to download,
250 so the process might take some time.
252 Please remember to shut the virtual machine down once your work on
259 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
261 Starting Tails builder virtual machine. This might take a short while.
262 Please remember to shut it down once your work on Tails is done:
268 if ENV['TAILS_RAM_BUILD'] && current_vm_memory < VM_MEMORY_FOR_RAM_BUILDS
269 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
271 The virtual machine is not currently set with enough memory to
272 perform an in-memory build. Either remove the `ram` option from
273 the TAILS_BUILD_OPTIONS environment variable, or shut the
274 virtual machine down using `rake vm:halt` before trying again.
277 abort 'Not enough memory for the virtual machine to run an in-memory build. Aborting.'
279 if ENV['TAILS_BUILD_CPUS'] && current_vm_cpus != ENV['TAILS_BUILD_CPUS'].to_i
280 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
282 The virtual machine is currently running with #{current_vm_cpus}
283 virtual CPU(s). In order to change that number, you need to
284 stop the VM first, using `rake vm:halt`. Otherwise, please
285 adjust the `cpus` options accordingly.
288 abort 'The virtual machine needs to be reloaded to change the number of CPUs. Aborting.'
291 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
292 result = env.cli('up')
293 abort "'vagrant up' failed" unless result
295 ENV['http_proxy'] = INTERNEL_HTTP_PROXY if restore_internal_proxy
298 desc 'Stop the build virtual machine'
300 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
301 result = env.cli('halt')
302 abort "'vagrant halt' failed" unless result
305 desc 'Re-run virtual machine setup'
306 task :provision => ['parse_build_options', 'validate_http_proxy'] do
307 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
308 result = env.cli('provision')
309 abort "'vagrant provision' failed" unless result
312 desc 'Destroy build virtual machine (clean up all files)'
314 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
315 result = env.cli('destroy', '--force')
316 abort "'vagrant destroy' failed" unless result
320 namespace :basebox do
321 task :create_preseed_cfg => 'validate_http_proxy' do
324 preseed_cfg_path = File.expand_path('../vagrant/definitions/squeeze/preseed.cfg', __FILE__)
325 template = ERB.new(File.read("#{preseed_cfg_path}.erb"))
326 File.open(preseed_cfg_path, 'w') do |f|
327 f.write template.result
331 desc 'Create virtual machine template (a.k.a. basebox)'
332 task :create_basebox => [:create_preseed_cfg] do
333 # veewee is pretty stupid regarding path handling
334 Dir.chdir(VAGRANT_PATH) do
337 # Veewee assumes a separate process for each task. So we mimic that.
339 env = Vagrant::Environment.new(:ui_class => Vagrant::UI::Basic)
342 env.cli('basebox', 'build', 'squeeze')
345 abort "Building the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
348 env.cli('basebox', 'validate', 'squeeze')
351 abort "Validating the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
354 env.cli('basebox', 'export', 'squeeze')
357 abort "Exporting the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0