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'
29 # Path to the directory which holds our Vagrantfile
30 VAGRANT_PATH = File.expand_path('../vagrant', __FILE__)
32 # Branches that are considered 'stable' (used to select SquashFS compression)
33 STABLE_BRANCH_NAMES = ['stable', 'testing']
35 # Environment variables that will be exported to the build script
36 EXPORTED_VARIABLES = ['http_proxy', 'MKSQUASHFS_OPTIONS', 'TAILS_RAM_BUILD', 'TAILS_CLEAN_BUILD', 'TAILS_BOOTSTRAP_CACHE']
38 # Let's save the http_proxy set before playing with it
39 EXTERNAL_HTTP_PROXY = ENV['http_proxy']
42 INTERNEL_HTTP_PROXY = "http://#{VIRTUAL_MACHINE_HOSTNAME}:3142"
45 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
46 uuid = env.primary_vm.uuid
47 info = env.primary_vm.driver.execute 'showvminfo', uuid, '--machinereadable'
48 $1.to_i if info =~ /^memory=(\d+)/
52 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
53 uuid = env.primary_vm.uuid
54 info = env.primary_vm.driver.execute 'showvminfo', uuid, '--machinereadable'
55 $1.to_i if info =~ /^cpus=(\d+)/
59 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
60 env.primary_vm.state == :running
63 def enough_free_memory?
64 return false unless RbConfig::CONFIG['host_os'] =~ /linux/i
67 usable_free_mem = `free`.split[16].to_i
68 usable_free_mem > VM_MEMORY_FOR_RAM_BUILDS * 1024
75 branch_name = `git name-rev --name-only HEAD`
76 tag_name = `git describe --exact-match HEAD 2> /dev/null`
77 STABLE_BRANCH_NAMES.include? branch_name.chomp or tag_name.chomp.length > 0
81 return nil unless RbConfig::CONFIG['host_os'] =~ /linux/i
84 File.read('/proc/cpuinfo').scan(/^processor\s+:/).count
90 task :parse_build_options do
93 # Default to in-memory builds if there is enough RAM available
94 options += 'ram ' if enough_free_memory?
96 # Use in-VM proxy unless an external proxy is set
97 options += 'vmproxy ' unless EXTERNAL_HTTP_PROXY
99 # Default to fast compression on development branches
100 options += 'gzipcomp ' unless is_release?
102 # Make sure release builds are clean
103 options += 'cleanall ' if is_release?
105 # Default to the number of system CPUs when we can figure it out
107 options += "cpus=#{cpus} " if cpus
109 options += ENV['TAILS_BUILD_OPTIONS'] if ENV['TAILS_BUILD_OPTIONS']
110 options.split(' ').each do |opt|
112 # Memory build settings
114 unless vm_running? || enough_free_memory?
115 abort "Not enough free memory to do an in-memory build. Aborting."
117 ENV['TAILS_RAM_BUILD'] = '1'
119 ENV['TAILS_RAM_BUILD'] = nil
120 # Bootstrap cache settings
122 ENV['TAILS_BOOTSTRAP_CACHE'] = '1'
124 ENV['TAILS_BOOTSTRAP_CACHE'] = nil
125 # HTTP proxy settings
127 abort "No HTTP proxy set, but one is required by TAILS_BUILD_OPTIONS. Aborting." unless EXTERNAL_HTTP_PROXY
128 ENV['http_proxy'] = EXTERNAL_HTTP_PROXY
130 ENV['http_proxy'] = INTERNEL_HTTP_PROXY
132 ENV['http_proxy'] = nil
133 # SquashFS compression settings
135 ENV['MKSQUASHFS_OPTIONS'] = '-comp gzip'
137 ENV['MKSQUASHFS_OPTIONS'] = nil
140 ENV['TAILS_CLEAN_BUILD'] = '1'
141 # Virtual CPUs settings
143 ENV['TAILS_BUILD_CPUS'] = $1
146 ENV['TAILS_BUILD_IGNORE_CHANGES'] = '1'
151 task :ensure_clean_repository do
152 unless `git status --porcelain`.empty?
153 if ENV['TAILS_BUILD_IGNORE_CHANGES']
154 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
156 You have uncommited changes in the Git repository. They will
157 be ignored for the upcoming build.
161 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
163 You have uncommited changes in the Git repository. Due to limitations
164 of the build system, you need to commit them before building Tails.
166 If you don't care about those changes and want to build Tails nonetheless,
167 please add `ignorechanges` to the TAILS_BUILD_OPTIONS environment
171 abort 'Uncommited changes. Aborting.'
176 task :validate_http_proxy do
178 proxy_host = URI.parse(ENV['http_proxy']).host
181 ENV['http_proxy'] = nil
182 $stderr.puts "Ignoring invalid HTTP proxy."
186 if ['localhost', '[::1]'].include?(proxy_host) || proxy_host.start_with?('127.0.0.')
187 abort 'Using an HTTP proxy listening on the loopback is doomed to fail. Aborting.'
190 $stderr.puts "Using HTTP proxy: #{ENV['http_proxy']}"
192 $stderr.puts "No HTTP proxy set."
197 task :build => ['parse_build_options', 'ensure_clean_repository', 'validate_http_proxy', 'vm:up'] do
198 exported_env = EXPORTED_VARIABLES.select { |k| ENV[k] }.
199 collect { |k| "#{k}='#{ENV[k]}'" }.join(' ')
201 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH)
202 status = env.primary_vm.channel.execute("#{exported_env} build-tails",
203 :error_check => false) do |fd, data|
204 (fd == :stdout ? $stdout : $stderr).write data
207 # Move build products to the current directory
208 FileUtils.mv Dir.glob("#{VAGRANT_PATH}/tails-*"),
209 File.expand_path('..', __FILE__), :force => true
215 desc 'Start the build virtual machine'
216 task :up => ['parse_build_options', 'validate_http_proxy'] do
217 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
218 case env.primary_vm.state
220 # Do not use non-existant in-VM proxy to download the basebox
221 if ENV['http_proxy'] == INTERNEL_HTTP_PROXY
222 ENV['http_proxy'] = nil
223 restore_internal_proxy = true
226 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
228 This is the first time that the Tails builder virtual machine is
229 started. The virtual machine template is about 300 MB to download,
230 so the process might take some time.
232 Please remember to shut the virtual machine down once your work on
239 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
241 Starting Tails builder virtual machine. This might take a short while.
242 Please remember to shut it down once your work on Tails is done:
248 if ENV['TAILS_RAM_BUILD'] && current_vm_memory < VM_MEMORY_FOR_RAM_BUILDS
249 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
251 The virtual machine is not currently set with enough memory to
252 perform an in-memory build. Either remove the `ram` option from
253 the TAILS_BUILD_OPTIONS environment variable, or shut the
254 virtual machine down using `rake vm:halt` before trying again.
257 abort 'Not enough memory for the virtual machine to run an in-memory build. Aborting.'
259 if ENV['TAILS_BUILD_CPUS'] && current_vm_cpus != ENV['TAILS_BUILD_CPUS'].to_i
260 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
262 The virtual machine is currently running with #{current_vm_cpus}
263 virtual CPU(s). In order to change that number, you need to
264 stop the VM first, using `rake vm:halt`. Otherwise, please
265 adjust the `cpus` options accordingly.
268 abort 'The virtual machine needs to be reloaded to change the number of CPUs. Aborting.'
271 result = env.cli('up')
272 abort "'vagrant up' failed" unless result
274 ENV['http_proxy'] = INTERNEL_HTTP_PROXY if restore_internal_proxy
277 desc 'Stop the build virtual machine'
279 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
280 result = env.cli('halt')
281 abort "'vagrant halt' failed" unless result
284 desc 'Re-run virtual machine setup'
285 task :provision => ['parse_build_options', 'validate_http_proxy'] do
286 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
287 result = env.cli('provision')
288 abort "'vagrant provision' failed" unless result
291 desc 'Destroy build virtual machine (clean up all files)'
293 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
294 result = env.cli('destroy', '--force')
295 abort "'vagrant destroy' failed" unless result
299 namespace :basebox do
300 task :create_preseed_cfg => 'validate_http_proxy' do
303 preseed_cfg_path = File.expand_path('../vagrant/definitions/squeeze/preseed.cfg', __FILE__)
304 template = ERB.new(File.read("#{preseed_cfg_path}.erb"))
305 File.open(preseed_cfg_path, 'w') do |f|
306 f.write template.result
310 desc 'Create virtual machine template (a.k.a. basebox)'
311 task :create_basebox => [:create_preseed_cfg] do
312 # veewee is pretty stupid regarding path handling
313 Dir.chdir(VAGRANT_PATH) do
316 # Veewee assumes a separate process for each task. So we mimic that.
318 env = Vagrant::Environment.new(:ui_class => Vagrant::UI::Basic)
321 env.cli('basebox', 'build', 'squeeze')
324 abort "Building the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
327 env.cli('basebox', 'validate', 'squeeze')
330 abort "Validating the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
333 env.cli('basebox', 'export', 'squeeze')
336 abort "Exporting the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0