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
65 return primary_vm.channel
67 return primary_vm.communicate
84 primary_vm.provider.driver
89 info = vm_driver.execute 'showvminfo', vm_id, '--machinereadable'
90 $1.to_i if info =~ /^cpus=(\d+)/
94 primary_vm_state == :running
97 def enough_free_host_memory_for_ram_build?
98 return false unless RbConfig::CONFIG['host_os'] =~ /linux/i
101 usable_free_mem = `free`.split[16].to_i
102 usable_free_mem > VM_MEMORY_FOR_RAM_BUILDS * 1024
109 primary_vm_chan.execute("free", :error_check => false) do |fd, data|
110 return data.split[16].to_i
114 def enough_free_vm_memory_for_ram_build?
115 free_vm_memory > BUILD_SPACE_REQUIREMENT * 1024
118 def enough_free_memory_for_ram_build?
120 enough_free_vm_memory_for_ram_build?
122 enough_free_host_memory_for_ram_build?
127 branch_name = `git name-rev --name-only HEAD`
128 tag_name = `git describe --exact-match HEAD 2> /dev/null`
129 STABLE_BRANCH_NAMES.include? branch_name.chomp or tag_name.chomp.length > 0
133 return nil unless RbConfig::CONFIG['host_os'] =~ /linux/i
136 File.read('/proc/cpuinfo').scan(/^processor\s+:/).count
142 task :parse_build_options do
145 # Default to in-memory builds if there is enough RAM available
146 options += 'ram ' if enough_free_memory_for_ram_build?
148 # Use in-VM proxy unless an external proxy is set
149 options += 'vmproxy ' unless EXTERNAL_HTTP_PROXY
151 # Default to fast compression on development branches
152 options += 'gzipcomp ' unless is_release?
154 # Make sure release builds are clean
155 options += 'cleanall ' if is_release?
157 # Default to the number of system CPUs when we can figure it out
159 options += "cpus=#{cpus} " if cpus
161 options += ENV['TAILS_BUILD_OPTIONS'] if ENV['TAILS_BUILD_OPTIONS']
162 options.split(' ').each do |opt|
164 # Memory build settings
166 ENV['TAILS_RAM_BUILD'] = '1'
168 ENV['TAILS_RAM_BUILD'] = nil
169 # Bootstrap cache settings
171 ENV['TAILS_BOOTSTRAP_CACHE'] = '1'
173 ENV['TAILS_BOOTSTRAP_CACHE'] = nil
174 # HTTP proxy settings
176 abort "No HTTP proxy set, but one is required by TAILS_BUILD_OPTIONS. Aborting." unless EXTERNAL_HTTP_PROXY
177 ENV['http_proxy'] = EXTERNAL_HTTP_PROXY
179 ENV['http_proxy'] = INTERNEL_HTTP_PROXY
181 ENV['http_proxy'] = nil
182 # SquashFS compression settings
184 ENV['MKSQUASHFS_OPTIONS'] = '-comp gzip'
186 ENV['MKSQUASHFS_OPTIONS'] = nil
189 ENV['TAILS_CLEAN_BUILD'] = '1'
190 # Virtual CPUs settings
192 ENV['TAILS_BUILD_CPUS'] = $1
195 ENV['TAILS_BUILD_IGNORE_CHANGES'] = '1'
200 task :ensure_clean_repository do
201 unless `git status --porcelain`.empty?
202 if ENV['TAILS_BUILD_IGNORE_CHANGES']
203 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
205 You have uncommited changes in the Git repository. They will
206 be ignored for the upcoming build.
210 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
212 You have uncommited changes in the Git repository. Due to limitations
213 of the build system, you need to commit them before building Tails.
215 If you don't care about those changes and want to build Tails nonetheless,
216 please add `ignorechanges` to the TAILS_BUILD_OPTIONS environment
220 abort 'Uncommited changes. Aborting.'
225 task :validate_http_proxy do
227 proxy_host = URI.parse(ENV['http_proxy']).host
230 ENV['http_proxy'] = nil
231 $stderr.puts "Ignoring invalid HTTP proxy."
235 if ['localhost', '[::1]'].include?(proxy_host) || proxy_host.start_with?('127.0.0.')
236 abort 'Using an HTTP proxy listening on the loopback is doomed to fail. Aborting.'
239 $stderr.puts "Using HTTP proxy: #{ENV['http_proxy']}"
241 $stderr.puts "No HTTP proxy set."
246 task :build => ['parse_build_options', 'ensure_clean_repository', 'validate_http_proxy', 'vm:up'] do
248 if ENV['TAILS_RAM_BUILD'] && not(enough_free_memory_for_ram_build?)
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.'
260 if ENV['TAILS_BUILD_CPUS'] && current_vm_cpus != ENV['TAILS_BUILD_CPUS'].to_i
261 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
263 The virtual machine is currently running with #{current_vm_cpus}
264 virtual CPU(s). In order to change that number, you need to
265 stop the VM first, using `rake vm:halt`. Otherwise, please
266 adjust the `cpus` options accordingly.
269 abort 'The virtual machine needs to be reloaded to change the number of CPUs. Aborting.'
272 exported_env = EXPORTED_VARIABLES.select { |k| ENV[k] }.
273 collect { |k| "#{k}='#{ENV[k]}'" }.join(' ')
274 status = primary_vm_chan.execute("#{exported_env} build-tails",
275 :error_check => false) do |fd, data|
276 (fd == :stdout ? $stdout : $stderr).write data
279 # Move build products to the current directory
280 FileUtils.mv Dir.glob("#{VAGRANT_PATH}/tails-*"),
281 File.expand_path('..', __FILE__), :force => true
287 desc 'Start the build virtual machine'
288 task :up => ['parse_build_options', 'validate_http_proxy'] do
289 case primary_vm_state
291 # Do not use non-existant in-VM proxy to download the basebox
292 if ENV['http_proxy'] == INTERNEL_HTTP_PROXY
293 ENV['http_proxy'] = nil
294 restore_internal_proxy = true
297 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
299 This is the first time that the Tails builder virtual machine is
300 started. The virtual machine template is about 300 MB to download,
301 so the process might take some time.
303 Please remember to shut the virtual machine down once your work on
310 $stderr.puts <<-END_OF_MESSAGE.gsub(/^ /, '')
312 Starting Tails builder virtual machine. This might take a short while.
313 Please remember to shut it down once your work on Tails is done:
319 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
320 result = env.cli('up')
321 abort "'vagrant up' failed" unless result
323 ENV['http_proxy'] = INTERNEL_HTTP_PROXY if restore_internal_proxy
326 desc 'Stop the build virtual machine'
328 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
329 result = env.cli('halt')
330 abort "'vagrant halt' failed" unless result
333 desc 'Re-run virtual machine setup'
334 task :provision => ['parse_build_options', 'validate_http_proxy'] do
335 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
336 result = env.cli('provision')
337 abort "'vagrant provision' failed" unless result
340 desc 'Destroy build virtual machine (clean up all files)'
342 env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
343 result = env.cli('destroy', '--force')
344 abort "'vagrant destroy' failed" unless result