aboutsummaryrefslogtreecommitdiff
path: root/rakelib/gcc.rake
blob: 7e00d1b7621eac57d2f59b4cdad9a9880eced000 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/. -}
# frozen_string_literal: true

require 'uri'
require 'net/http'
require 'open3'
require 'pathname'

def gcc_verbose(gcc_binary)
  read, write = IO.pipe
  sh({'LC_ALL' => 'C'}, gcc_binary, '--verbose', err: write)
  write.close
  output = read.read
  read.close
  output
end

def find_build_target
  gcc_verbose(ENV.fetch 'CC', 'gcc')
    .lines
    .find { |line| line.start_with? 'Target: ' }
    .split(' ')
    .last
    .strip
end

def download_and_pipe(url, target, command)
  target.mkpath

  Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
    request = Net::HTTP::Get.new url.request_uri

    http.request request do |response|
      case response
      when Net::HTTPRedirection
        download_and_pipe URI.parse(response['location']), target, command
      when Net::HTTPSuccess
        Dir.chdir target.to_path do
          Open3.popen2(*command) do |stdin, stdout, wait_thread|
            Thread.new do
              stdout.each { |line| puts line }
            end

            response.read_body do |chunk|
              stdin.write chunk
            end
            stdin.close

            wait_thread.value
          end
        end
      else
        response.error!
      end
    end
  end
end

def link_frontend(source, destination)
  File.symlink Pathname.new(source).relative_path_from(destination), (destination + File.basename(source))
end

namespace :gcc do
  # Dependencies.
  GCC_VERSION = "15.3.0"
  HOST_GCC = 'build/host/gcc'
  GCC_TREE = Pathname.new "build/tools/gcc-#{GCC_VERSION}"
  GCC_PATCH = 'https://raw.githubusercontent.com/Homebrew/homebrew-core/refs/heads/main/Patches/gcc/gcc-15.3.0.diff'

  directory HOST_GCC
  directory 'build/host/install'
  directory 'build/tools'

  desc 'Download the bootstrap compiler and its prerequisites'
  task download: 'build/tools' do
    url = URI.parse "https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"

    download_and_pipe url, GCC_TREE.dirname, ['tar', '-Jxv']
    download_and_pipe URI.parse(GCC_PATCH), GCC_TREE, ['patch', '-p1']

    sh 'contrib/download_prerequisites', chdir: GCC_TREE.to_path
  end

  desc 'Link the frontend into the GCC source tree'
  task :link do
    source_destination = GCC_TREE + 'gcc/elna'
    test_destination = GCC_TREE + 'gcc/testsuite/elna.dg'

    rm_rf [source_destination, test_destination]
    mkdir_p [source_destination, test_destination]

    FileList['boot', 'include', 'COPYING3', 'README.md', 'gcc/gcc', 'gcc/*.in', 'gcc/lang*'].each do |file|
      link_frontend file, source_destination
    end
    FileList['testsuite/*', 'gcc/dg.exp', 'README.md'].each do |file|
      link_frontend file, test_destination
    end
    destination = GCC_TREE + 'gcc/testsuite/lib'
    FileList['gcc/testlib/*'].each do |file|
      rm_f (destination + File.basename(file))
      link_frontend file, destination
    end
  end

  desc 'Configure the bootstrap compiler'
  task configure: [HOST_GCC, 'build/host/install'] do |t|
    build_target = find_build_target
    configure_options = [
      "--prefix=#{File.realpath t.prerequisites.last}",
      '--enable-languages=c,c++,jit,elna',
      '--disable-bootstrap',
      '--disable-multilib',
      '--enable-host-shared',
      '--with-system-zlib',
      "--target=#{build_target}",
      "--build=#{build_target}",
      "--host=#{build_target}"
    ]
    mac_os_sdk = '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk'
    configure_options << "--with-sysroot=#{mac_os_sdk}" if File.symlink? mac_os_sdk

    env = ENV.slice 'CC', 'CXX'
    env['CFLAGS'] = env['CXXFLAGS'] = '-O0 -g -fPIC -I/opt/homebrew/opt/flex/include'

    configure = GCC_TREE.relative_path_from(HOST_GCC) + 'configure'
    sh env, configure.to_path, *configure_options, chdir: HOST_GCC
  end

  desc 'Make and install the bootstrap compiler'
  task :make do
    sh 'make', '-j', Etc.nprocessors.to_s, chdir: HOST_GCC
    sh 'make', 'install', chdir: HOST_GCC
  end

  desc 'Run tests'
  task :check do
    sh 'make', 'check-elna', chdir: File.join(HOST_GCC, 'gcc')
  end
end

desc 'Build the bootstrap compiler'
task gcc: %w[gcc:download gcc:link gcc:configure gcc:make]