#!/usr/bin/perl use strict; use warnings; use FindBin qw($Bin $Script); use File::Path; my $GNU_MIRROR = 'ftp://ftp.fu-berlin.de/unix/gnu'; my $NEWLIB_URL = 'ftp://sources.redhat.com/pub/newlib'; my $GCC_VERSION = '4.6.3'; my $GDB_VERSION = '7.4'; my $BINUTILS_VERSION = '2.22'; my $NEWLIB_VERSION = '1.20.0'; my $OPENOCD_VERSION = '0.5.0'; my $GCC_URL = "$GNU_MIRROR/gcc/gcc-$GCC_VERSION"; my $GDB_URL = "$GNU_MIRROR/gdb"; my $BINUTILS_URL = "$GNU_MIRROR/binutils"; my $issue = 'this OS'; if (open I, "; close I; $issue =~ s/\\.*$//g; $issue =~ s/\s+$//g; } if ($issue =~ /^Ubuntu/) { if ($issue !~ /^Ubuntu 11.10/) { warn "Notice that this script hasn't been tested on $issue, if it fails, try Ubuntu 11.10 in stead\n"; } my @missing; for my $p qw'build-essential zlib1g-dev libgmp-dev libmpfr-dev libmpc-dev texinfo libftdi-dev libncurses5-dev libreadline-dev libexpat1-dev' { print "Checking that you have $p\n"; push @missing, $p if system("dpkg -s $p > /dev/null"); } if (@missing) { my $cmd = "sudo apt-get install ".join(' ', @missing); print "You're missing some packages, trying to fix that with: $cmd\n"; system($cmd) and die "Failed to install missing packages, please run this command manually: $cmd"; } } else { warn "Notice that this script hasn't been tested on $issue, if it fails, try Ubuntu 11.10 in stead\n"; } my @DIRS = ( { dir => "binutils-$BINUTILS_VERSION", url => $BINUTILS_URL, cfg => ['--target=arm-none-eabi', '--enable-interwork', '--enable-multilib'], make => 'make all install', }, { dir => "gcc-$GCC_VERSION", url => $GCC_URL, cfg => ['--target=arm-none-eabi', '--enable-interwork', '--enable-multilib', '--enable-languages=c,c++', '--with-newlib', "--with-headers=../../src/newlib-$NEWLIB_VERSION/newlib/libc/include", '--with-system-zlib', '--with-cpu=cortex-m3', '--with-mode=thumb', ], make=>'make all-gcc install-gcc', make2=>'make all install', }, { dir => "gcc-core-$GCC_VERSION", url => $GCC_URL, }, { dir => "gcc-g++-$GCC_VERSION", url => $GCC_URL, }, { dir => "newlib-$NEWLIB_VERSION", file => "newlib-$NEWLIB_VERSION.tar.gz", url => $NEWLIB_URL, cfg => ['--target=arm-none-eabi', '--enable-interwork', '--enable-multilib', '--with-cpu=cortex-m3', '--with-mode=thumb', '--disable-newlib-supplied-syscalls' ], make => 'make all install', }, { dir => "gdb-$GDB_VERSION", url => $GDB_URL, cfg2=>['--target=arm-none-eabi', '--enable-interwork', '--enable-multilib'], make2=>'make all install', }, { dir=> "openocd-$OPENOCD_VERSION", url=>"http://downloads.sourceforge.net/project/openocd/openocd/$OPENOCD_VERSION/openocd-$OPENOCD_VERSION.tar.bz2?use_mirror=autoselect", inplace=>1, patch=>'openocd-0.5.0-fix.patch', cfg=>['--enable-ft2232_libftdi', '--enable-jlink', '--enable-buspirate' ], make=>'make all install', }, ); my $tarDir = "$Bin/tarballs"; mkdir $tarDir; # Do all the downloading up front, so we get that out of the way. for my $d (@DIRS) { $d->{file} //= "$d->{dir}.tar.bz2"; my $fn = "$tarDir/$d->{file}"; my $url = "$d->{url}/$d->{file}"; if (-f $fn) { print "You already have $d->{file}, skipping\n"; } else { system("wget", "-O", $fn, $url) and die "Failed to fetch $d->{file} from $url"; die "Did not find the expected file: $fn" unless -f $fn; } } # Unpack, configure, build and install each package. my $srcDir = "$Bin/src"; mkdir $srcDir; my $buildDir = "$Bin/build"; mkdir $buildDir; my $installDir = "$Bin/arm-toolchain"; mkdir $installDir; $ENV{PATH} = "$ENV{PATH}:$installDir/bin"; # Unpack all at sources once. for my $d (@DIRS) { my $fn = "$tarDir/$d->{file}"; my $dn = "$srcDir/$d->{dir}"; if (-d $dn) { print "Already unpacked $d->{dir}, skipping unpack\n"; } else { print "Unpacking $fn\n"; chdir $srcDir; system("tar", "xf", $fn) and die "Failed to unpack $fn into $srcDir"; mkdir $dn; if ($d->{patch}) { chdir $dn; system("patch -p 1 < $Bin/$d->{patch}") and die "Failed to apply patch: $d->{patch}"; } } } # Do the actual building, first pass for my $d (@DIRS) { my $fn = "$tarDir/$d->{file}"; my $dn = "$srcDir/$d->{dir}"; my $bn = "$buildDir/$d->{dir}"; next if -f "$bn.done"; # Lazyness ftw. rmtree $bn; mkdir $bn; if ($d->{inplace}) { chdir $dn; # Build in the source directory, needed for borken packages } else { chdir $bn; # Build in a separate tree, because it's nice not to mess up the source tree. } next unless $d->{make} and $d->{cfg}; my @cfg = (@{$d->{cfg}}, "--prefix=$installDir"); print "Running: $bn> $dn/configure ".join(' ',@cfg)."\n"; system("$dn/configure", @cfg) and die "Failed to configure in $bn"; print "Running: $bn> $d->{make}\n"; system($d->{make}) and die "Failed to run $d->{make} in $bn"; open D, ">$bn.done"; close D; } # Second pass, needed because we need to hit gcc twice, before and after building newlib. for my $d (@DIRS) { my $fn = "$tarDir/$d->{file}"; my $dn = "$srcDir/$d->{dir}"; my $bn = "$buildDir/$d->{dir}"; next unless $d->{make2}; next if -f "$bn.done2"; # Lazyness ftw. chdir $bn; if ($d->{cfg2}) { my @cfg = (@{$d->{cfg2}}, "--prefix=$installDir"); print "Running2: $bn> $dn/configure ".join(' ',@cfg)."\n"; system("$dn/configure", @cfg) and die "Failed to configure in $bn"; } print "Running2: $bn> $d->{make2}\n"; system($d->{make2}) and die "Failed to run2 $d->{make2} in $bn"; open D, ">$bn.done2"; close D; } print "\n\nAll done, toolchain can be found in $installDir\n"; print "Add to path with: export PATH=\${PATH}:$installDir/bin\n"; exit 0;