4 # Tails: The Amnesic Incognito Live System
5 # Copyright © 2012 Tails developers <tails@boum.org>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 $:.unshift File.expand_path('../vagrant/lib', __FILE__)
26 require 'tails_build_settings'
28 # Path to the directory which holds our Vagrantfile
29 VAGRANT_PATH = File.expand_path('../vagrant', __FILE__)
31 # Branches that are considered 'stable' (used to select SquashFS compression)
32 STABLE_BRANCH_NAMES = ['stable', 'testing']
34 # Environment variables that will be exported to the build script
35 EXPORTED_VARIABLES = ['http_proxy', 'MKSQUASHFS_OPTIONS', 'TAILS_RAM_BUILD', 'TAILS_CLEAN_BUILD']
37 # Let's save the http_proxy set before playing with it
38 EXTERNAL_HTTP_PROXY = ENV['http_proxy']
41 INTERNEL_HTTP_PROXY = "http://#{VIRTUAL_MACHINE_HOSTNAME}:3142"
44 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
45 uuid = env.primary_vm.uuid
46 info = env.primary_vm.driver.execute 'showvminfo', uuid, '--machinereadable'
47 $1.to_i if info =~ /^memory=(\d+)/
51 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
52 uuid = env.primary_vm.uuid
53 info = env.primary_vm.driver.execute 'showvminfo', uuid, '--machinereadable'
54 $1.to_i if info =~ /^cpus=(\d+)/
57 def enough_free_memory?
58 return false unless RbConfig::CONFIG['host_os'] =~ /linux/i
61 usable_free_mem = `free`.split[16].to_i
62 usable_free_mem > VM_MEMORY_FOR_RAM_BUILDS * 1024
69 branch_name = `git name-rev --name-only HEAD`
70 STABLE_BRANCH_NAMES.include? branch_name
74 return nil unless RbConfig::CONFIG['host_os'] =~ /linux/i
77 File.read('/proc/cpuinfo').scan(/^processor\s+:/).count
83 task :parse_build_options do
86 # Default to in-memory builds if there is enough RAM available
87 options += 'ram ' if enough_free_memory?
89 # Use in-VM proxy unless an external proxy is set
90 options += 'vmproxy ' unless EXTERNAL_HTTP_PROXY
92 # Default to fast compression on development branches
93 options += 'gzipcomp ' unless stable_branch?
95 # Default to the number of system CPUs when we can figure it out
97 options += "cpus=#{cpus} " if cpus
99 options += ENV['TAILS_BUILD_OPTIONS'] if ENV['TAILS_BUILD_OPTIONS']
100 options.split(' ').each do |opt|
102 # Memory build settings
104 abort "Not enough free memory to do an in-memory build. Aborting." unless enough_free_memory?
105 ENV['TAILS_RAM_BUILD'] = '1'
107 ENV['TAILS_RAM_BUILD'] = nil
108 # HTTP proxy settings
110 abort "No HTTP proxy set, but one is required by TAILS_BUILD_OPTIONS. Aborting." unless EXTERNAL_HTTP_PROXY
111 ENV['http_proxy'] = EXTERNAL_HTTP_PROXY
113 ENV['http_proxy'] = INTERNEL_HTTP_PROXY
115 ENV['http_proxy'] = nil
116 # SquashFS compression settings
118 ENV['MKSQUASHFS_OPTIONS'] = '-comp gzip'
120 ENV['MKSQUASHFS_OPTIONS'] = nil
123 ENV['TAILS_CLEAN_BUILD'] = '1'
124 # Virtual CPUs settings
126 ENV['TAILS_BUILD_CPUS'] = $1
129 ENV['TAILS_BUILD_IGNORE_CHANGES'] = '1'
134 task :ensure_clean_repository do
135 unless `git status --porcelain`.empty?
136 if ENV['TAILS_BUILD_IGNORE_CHANGES']
137 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
139 You have uncommited changes in the Git repository. They will
140 be ignored for the upcoming build.
144 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
146 You have uncommited changes in the Git repository. Due to limitations
147 of the build system, you need to commit them before building Tails.
149 If you don't care about those changes and want to build Tails nonetheless,
150 please add `ignorechanges` to the TAILS_BUILD_OPTIONS environment
154 abort 'Uncommited changes. Aborting.'
159 task :validate_http_proxy do
161 proxy_host = URI.parse(ENV['http_proxy']).host
164 ENV['http_proxy'] = nil
165 $stderr.puts "Ignoring invalid HTTP proxy."
169 if ['localhost', '[::1]'].include?(proxy_host) || proxy_host.start_with?('127.0.0.')
170 abort 'Using an HTTP proxy listening on the loopback is doomed to fail. Aborting.'
173 $stderr.puts "Using HTTP proxy: #{ENV['http_proxy']}"
175 $stderr.puts "No HTTP proxy set."
180 task :build => ['parse_build_options', 'ensure_clean_repository', 'validate_http_proxy', 'vm:up'] do
181 exported_env = EXPORTED_VARIABLES.select { |k| ENV[k] }.
182 collect { |k| "#{k}='#{ENV[k]}'" }.join(' ')
184 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH)
185 status = env.primary_vm.channel.execute("#{exported_env} build-tails",
186 :error_check => false) do |fd, data|
187 (fd == :stdout ? $stdout : $stderr).write data
190 # Move build products to the current directory
191 FileUtils.mv Dir.glob("#{VAGRANT_PATH}/tails-*"),
192 File.expand_path('..', __FILE__), :force => true
198 desc 'Start the build virtual machine'
199 task :up => ['parse_build_options', 'validate_http_proxy'] do
200 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
201 case env.primary_vm.state
203 # Do not use non-existant in-VM proxy to download the basebox
204 if ENV['http_proxy'] == INTERNEL_HTTP_PROXY
205 ENV['http_proxy'] = nil
206 restore_internal_proxy = true
209 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
211 This is the first time that the Tails builder virtual machine is
212 started. The virtual machine template is about 300 MB to download,
213 so the process might take some time.
215 Please remember to shut the virtual machine down once your work on
222 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
224 Starting Tails builder virtual machine. This might take a short while.
225 Please remember to shut it down once your work on Tails in done:
231 if ENV['TAILS_RAM_BUILD'] && current_vm_memory < VM_MEMORY_FOR_RAM_BUILDS
232 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
234 The virtual machine is not currently set with enough memory to
235 perform an in-memory build. Either remove the `ram` option from
236 the TAILS_BUILD_OPTIONS environment variable, or shut the
237 virtual machine down using `rake vm:halt` before trying again.
240 abort 'Not enough memory for the virtual machine to run an in-memory build. Aborting.'
242 if ENV['TAILS_BUILD_CPUS'] && current_vm_cpus != ENV['TAILS_BUILD_CPUS'].to_i
243 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
245 The virtual machine is currently running with #{current_vm_cpus}
246 virtual CPU(s). In order to change that number, you need to
247 stop the VM first, using `rake vm:halt`. Otherwise, please
248 adjust the `cpus` options accordingly.
251 abort 'The virtual machine needs to be reloaded to change the number of CPUs. Aborting.'
254 result = env.cli('up')
255 abort "'vagrant up' failed" unless result
257 ENV['http_proxy'] = INTERNEL_HTTP_PROXY if restore_internal_proxy
260 desc 'Stop the build virtual machine'
262 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
263 result = env.cli('halt')
264 abort "'vagrant halt' failed" unless result
267 desc 'Re-run virtual machine setup'
268 task :provision => ['parse_build_options', 'validate_http_proxy'] do
269 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
270 result = env.cli('provision')
271 abort "'vagrant provision' failed" unless result
274 desc 'Destroy build virtual machine (clean up all files)'
276 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
277 result = env.cli('destroy', '--force')
278 abort "'vagrant destroy' failed" unless result
282 namespace :basebox do
283 task :create_preseed_cfg => 'validate_http_proxy' do
286 preseed_cfg_path = File.expand_path('../vagrant/definitions/squeeze/preseed.cfg', __FILE__)
287 template = ERB.new(File.read("#{preseed_cfg_path}.erb"))
288 File.open(preseed_cfg_path, 'w') do |f|
289 f.write template.result
293 desc 'Create virtual machine template (a.k.a. basebox)'
294 task :create_basebox => [:create_preseed_cfg] do
295 # veewee is pretty stupid regarding path handling
296 Dir.chdir(VAGRANT_PATH) do
299 # Veewee assumes a separate process for each task. So we mimic that.
301 env = Vagrant::Environment.new(:ui_class => Vagrant::UI::Basic)
304 env.cli('basebox', 'build', 'squeeze')
307 abort "Building the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
310 env.cli('basebox', 'validate', 'squeeze')
313 abort "Validating the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
316 env.cli('basebox', 'export', 'squeeze')
319 abort "Exporting the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0