├── .debian-build-imagerc.EXAMPLE ├── .gitignore ├── LICENSE ├── README.md ├── Vagrantfile ├── compile-image ├── debian-build-image ├── mint-build-image └── ubuntu-build-image /.debian-build-imagerc.EXAMPLE: -------------------------------------------------------------------------------- 1 | export TMUX_PANE='' 2 | #ROUTERMDL="RT-AC56U" 3 | 4 | hook_unknown_router() # $1 = 5 | { 6 | echo "[hook] user-supplied router model not recognized: '$1'" 7 | return 0 8 | } 9 | 10 | hook_override_model() # no arguments 11 | { 12 | # Inspect OLD_ROUTERMDL, ROUTERMDL, ARCH and change them as needed 13 | echo "OLD_ROUTERMDL=${OLD_ROUTERMDL}" 14 | echo "ROUTERMDL=${ROUTERMDL}" 15 | echo "ARCH=${ARCH}" 16 | } 17 | 18 | hook_ubuntu_extras() # no arguments 19 | { 20 | echo "Additional Ubuntu fixups" 21 | } 22 | 23 | hook_pre_fixups() # no arguments, check if USE_SUDO is non-empty 24 | { 25 | echo "Fixup steps before" 26 | } 27 | 28 | hook_post_fixups() # no arguments, check if USE_SUDO is non-empty 29 | { 30 | echo "Fixup steps after" 31 | } 32 | 33 | hook_suppress_missing_bin() # $1 = 34 | { 35 | return 0 # to suppress, nonzero to propagate failure and throw a fatal error 36 | } 37 | 38 | hook_pre_clean() # $1 = 39 | { 40 | echo "Called before 'make clean' is invoked" 41 | } 42 | 43 | hook_pre_make() # $1 = 44 | { 45 | echo "Called before 'make ' is invoked" 46 | } 47 | 48 | hook_post_make() # $1 = 49 | { 50 | echo "Called after make is invoked" 51 | } 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | release/* 2 | build.log 3 | asuswrt-merlin/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # build-asuswrt-merlin 2 | 3 | Helper scripts to build [RMerl/asuswrt-merlin](https://github.com/RMerl/asuswrt-merlin) without much extra typing. 4 | 5 | * Backlink to [Compiling from source using a Debian based Linux Distribution](https://github.com/RMerl/asuswrt-merlin/wiki/Compiling-from-source-using-a-Debian-based-Linux-Distribution) 6 | * Backlink to [Compile Firmware from source using Linux Mint](https://github.com/RMerl/asuswrt-merlin/wiki/Compile-Firmware-from-source-using-Linux-Mint) 7 | * Backlink to [Compile firmware from source using Ubuntu](https://github.com/RMerl/asuswrt-merlin/wiki/Compile-Firmware-from-source-using-Ubuntu) 8 | 9 | ## Summary 10 | 11 | Last tested on `master` from RMerl/asuswrt-merlin as of 2015-02-11. The script takes care of the steps mentioned above for Ubuntu 13.10 and newer. In addition it does fixups to the source tree which ensure that it can run without superuser rights as long as the user makes sure all the packages are installed. It will also check for the packages being installed and bail out with a meaningful error message if not. 12 | 13 | It can be run in two modes: with or without `sudo` involved. With `sudo` it will use the symbolic link method inside `/opt`, whereas without it will modify the files in the source tree to adjust hardcoded paths. 14 | 15 | If you are running inside a [`tmux`](http://tmux.sourceforge.net/) pane, this will also pipe the output into a log file (see _Environment variables_). 16 | 17 | ### Supported distributions 18 | 19 | * Debian 7.x (tested on 7.8) 20 | * Ubuntu 12.04 and above (tested on 12.04 and 14.04) 21 | * Linux Mint 13 and above (experimental, untested) 22 | 23 | All were amd64 (x86\_64). 24 | 25 | ## Syntax 26 | 27 | * Unprivileged: `./debian-build-image [path-to-asuswrt-merlin]` 28 | * With `sudo`: `USE_SUDO=1 ./debian-build-image [path-to-asuswrt-merlin]` 29 | 30 | You can leave out the `path-to-asuswrt-merlin` argument and the script will check the folder it resides in for a marker file (`README-merlin.txt`) to see whether this is the expected source tree. 31 | 32 | ### Supported router models 33 | 34 | The \*R and \*W variants of the routers are also supported. R is for retail versions and W for white models. The firmware is the same. The router name can be given in upper or lower case and you can leave out the trailing U, R or W. 35 | 36 | #### MIPS-based routers 37 | * RT-N16 38 | * RT-N66U 39 | * RT-AC66U 40 | 41 | #### ARM-based routers 42 | * RT-AC56U 43 | * RT-AC68U & RT-AC68U\_V2 44 | * RT-AC69U 45 | * RT-AC87U 46 | 47 | ### Environment variables 48 | 49 | Some environment variables influence how the script behaves: 50 | 51 | * Any non-empty value for `USE_SUDO` will cause the image creation process to assume that `sudo` is available to symlink the toolchain into `/opt`. This is useful if for some reason the unprivileged method fails or if you simply prefer to stick closer to the process explained on the RMerl/asuswrt-merlin wiki. 52 | * Any non-empty value for `FORCE_UNSUPPORTED` can be used to run the script on otherwise not explicitly supported Debian flavors. 53 | * `TMUX_PANE` is used to sense the presence of a `tmux` session and will turn on logging. To inhibit this behavior, make sure to either `unset` this variable or to give it an empty value during invocation (i.e. `TMUX_PANE= ./debian-build-image` ...). 54 | 55 | You can either set the environment variable globally by exporting it prior to the invocation, e.g.: 56 | 57 | ```bash 58 | export USE_SUDO=1 59 | ./debian-build-image RT-N66U 60 | ``` 61 | 62 | ... or by passing it on the command line like so: 63 | 64 | ```bash 65 | USE_SUDO=1 ./debian-build-image RT-N66U 66 | ``` 67 | 68 | ### Special syntax for prerequisites 69 | 70 | In order to have the script install all the prerequisites using `apt-get`, use the following method: 71 | 72 | * `./debian-build-image -p ` (or `--prereq` instead of `-p`) 73 | 74 | Please note that this requires you to be a sudoer. Usually that means you need to be a member of the group `sudo` on `.deb`-based distros or `wheel` on `.rpm`-based distros. 75 | 76 | **NOTE:** Strictly speaking the command should also work without the router model given, but it may miss some model-specific prerequisites in such a case. As an example: ARM routers require `libelf1:i386` because the `gcc` of the ARM toolchain depends on it. 77 | 78 | ## Usage on Debian, Linux Mint and Ubuntu 79 | 80 | ### Preparation 81 | 82 | * Verify you have `git` installed, otherwise do `sudo apt-get --no-install-recommends install git git-man`. 83 | * Change into a folder inside of which you'd like the repository to reside (e.g. `~`). 84 | * Clone the RMerl/asuswrt-merlin source: 85 | * via SSH: `git clone git@github.com:RMerl/asuswrt-merlin.git` 86 | * via HTTPS: `git clone https://github.com/RMerl/asuswrt-merlin.git` 87 | * Clone this repository or download the raw `debian-build-image` script: 88 | * via SSH: `git clone git@github.com:assarbad/build-asuswrt-merlin.git` 89 | * via HTTPS: `git clone https://github.com/assarbad/build-asuswrt-merlin.git` 90 | * Download `wget https://raw.githubusercontent.com/assarbad/build-asuswrt-merlin/master/debian-build-image && chmod +x debian-build-image` 91 | * Copy `debian-build-image` into the clone of RMerl/asuswrt-merlin (e.g. `~/asuswrt-merlin`). 92 | * Change into the RMerl/asuswrt-merlin directory (e.g. `~/asuswrt-merlin`) and invoke `./debian-build-image --prereq` there. 93 | * This will prompt you to execute a `sudo` command to install all prerequisites. This command does not check before whether some packages are missing, though. Remember that this requires you to be a sudoer! 94 | 95 | ### Building an image 96 | 97 | Well, the syntax has been explained above. Running the script is as easy as invoking (from our previous example): 98 | 99 | ~/asuswrt-merlin/debian-build-image RT-N66U 100 | 101 | The name of the router is case-insensitive. Also, it handles the models with their U, R, and W prefixes. 102 | 103 | At this point you'll probably want to take a break and come back later. This will take quite a while. 104 | 105 | ### Starting over 106 | 107 | * Change into the clone directory 108 | * You'll want to issue a `git reset --hard` inside the cloned repository to undo all the changes `debian-build-image` does 109 | * And then start over after the preparation steps above 110 | 111 | ## Usage with Vagrant 112 | 113 | It should be easiest way to build firmware in virtual machine using [Vagrant](http://www.vagrantup.com/). 114 | 115 | First run a virtual machine: 116 | 117 | vagrant up 118 | 119 | Second look for some firmware release version on [releases page](https://github.com/RMerl/asuswrt-merlin/releases) and build an image for desired router model: 120 | 121 | vagrant ssh -- compile-image -m RT-N66U -r 378.55 122 | 123 | Then wait for cloning sources from GitHub into VM, building an image and copying it to `release/` directory. 124 | 125 | You can build firmware from any Git branch, for example 'master': 126 | 127 | vagrant ssh -- compile-image -m RT-N66U -r master 128 | 129 | At the end, stop VM: 130 | 131 | vagrant halt 132 | 133 | or remove it completely: 134 | 135 | vagrant destroy 136 | 137 | ## Contribute 138 | 139 | Please contribute by reporting issues and sending pull requests. 140 | 141 | Oh and feel free to fork or embed this into your repository. 142 | 143 | ## License 144 | 145 | This work is released into the public domain. See LICENSE. If _public domain_ is not a recognized legal concept in your jurisdiction, treat as [CC0](https://creativecommons.org/publicdomain/zero/1.0/); which should have the proper wording for many jurisdictions. The disclaimer from `LICENSE` (second to last paragraph) still applies. 146 | 147 | If neither of these suite your needs, please ask by opening a ticket. 148 | 149 | ## Contact 150 | 151 | You can get in touch with me via my [contact form](https://assarbad.net/en/contact) (or one of the listed contact options there). 152 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | #-*- mode: ruby -*- 2 | # vi: set ft=ruby ai si et ts=2 sw=2 sts=2: 3 | # 4 | # Vagrant configuration to setup build enviroment for asuswrt-merlin project. 5 | 6 | # Tweak .bashrc of 'vagrant' user to put the host volume into $PATH 7 | SCRIPT=<<-SHELL 8 | sed -i 's!^# for examples!PATH="/vagrant:$PATH"!' "$HOME/.bashrc" 9 | SHELL 10 | 11 | 12 | Vagrant.configure(2) do |config| 13 | 14 | config.vm.box = "ubuntu/trusty32" 15 | config.vm.boot_timeout = 300 16 | config.vm.box_check_update = false 17 | config.vm.provision "shell", inline: SCRIPT, privileged: false 18 | 19 | config.vm.provider "virtualbox" do |v| 20 | v.memory = 1024 21 | v.cpus = 1 22 | v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 23 | end 24 | 25 | end 26 | 27 | -------------------------------------------------------------------------------- /compile-image: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # vim: set autoindent smartindent ts=4 sw=4 sts=4 noet filetype=sh: 3 | ############################################################################### 4 | ## This is provision/build helper script for Vagrant. 5 | ## Look in Vagrantfile for more details. 6 | ############################################################################### 7 | 8 | set -e 9 | 10 | ## Router model. 'RT-N66U', 'RT-AC66U', and etc. 11 | MODEL="" 12 | 13 | ## Tag, commit sha or branch on asuswrt-merlin repository is used to make the 14 | ## image. 15 | RELEASE="" 16 | 17 | ## The folder for storing sources. 18 | ## Note, it isn't possible to use '/vagrant' shared folder for sources now, 19 | ## because of the asuswrt-merlin build currently requires hardlinks. According 20 | ## to the virtualbox issue (https://www.virtualbox.org/ticket/818) they don't 21 | ## support hardlinks in shared folders, and they don't seem to plan on adding 22 | ## that. 23 | SRCDIR="/home/vagrant/asuswrt-merlin" 24 | 25 | ## The path of the directory in which the builed images will be stored. 26 | ## It is relative to $VOLUME thus images will be available on the host system. 27 | RELEASEDIR="release" 28 | 29 | ## The path of the Vagrant shared folder. 30 | VOLUME="/vagrant" 31 | 32 | ## The URL of the asuswrt-merlin Git repository. 33 | GITURL="https://github.com/RMerl/asuswrt-merlin" 34 | REMOTE=origin 35 | 36 | ## The name of the build script. 37 | BUILD_IMAGE="ubuntu-build-image" 38 | 39 | LOGFILE="/tmp/build.log" 40 | SELFNAME="${0##*/}" 41 | export DEBIAN_FRONTEND=noninteractive 42 | 43 | 44 | display_help() { 45 | echo "Usage: ${SELFNAME} [OPTION]..." 46 | echo "Build asuswrt-merlin image for ASUS router from sources." 47 | echo "Example: ${SELFNAME} -m RT-N66U -r 378.55" 48 | echo "" 49 | echo " -h, --help display this help and exit" 50 | echo " -m, --model optional router model name you'd build image" 51 | echo " -r, --release optional release specified by tag or branch in the git repository" 52 | echo " see https://github.com/RMerl/asuswrt-merlin/releases" 53 | echo " for more details Default is 'master'." 54 | echo "" 55 | echo "This script should be run in a virtual machine." 56 | } 57 | 58 | 59 | check_locale() ( 60 | # enshure all used locales are defined 61 | get_undefined_locales() { 62 | # this display locales in 'locale' output that are missed in 'locale -a' 63 | locale 2>/dev/null \ 64 | | awk -F '[=]' '/^LC_/{print $2}' | sed 's#"##g;' | grep . | sort | uniq \ 65 | | while read line; do 66 | locale -a | sed 's#[-]##g;' | tr '[:upper:]' '[:lower:]' \ 67 | | grep -q "$(echo $line | sed 's#[-]##g;' | tr '[:upper:]' '[:lower:]')" || echo $line 68 | done 69 | } 70 | undefined_locales="$(get_undefined_locales)" 71 | if [ -n "$undefined_locales" ]; then 72 | echo "Install locales" 73 | echo $undefined_locales | xargs sudo locale-gen && sudo dpkg-reconfigure locales 74 | fi 75 | ) 76 | 77 | 78 | provision() { 79 | echo "Enshure all requirements are installed" 80 | check_locale 81 | 82 | # enshure we are have 'git' installed 83 | if ! hash git 2> /dev/null; then 84 | echo "Install 'git' binary" 85 | sudo apt-get update -y 86 | sudo apt-get install -y --no-install-recommends git-core 87 | fi 88 | 89 | echo "Provision complete." 90 | } 91 | 92 | 93 | checkout_sources() { 94 | echo "Get asuswrt-merlin sources" 95 | 96 | # enshure we have asuswrt-merlin sources 97 | if [ ! -d "$SRCDIR" ]; then 98 | echo "Clone $GITURL repository" 99 | git clone "$GITURL" "$SRCDIR" 100 | fi 101 | 102 | cd "$SRCDIR" 103 | 104 | echo "Check $(git config --get remote.${REMOTE}.url) for updates" 105 | git fetch "$REMOTE" || echo "Skipped: remote repository does not reachable now." 106 | 107 | echo "Cleanup source directory" 108 | git reset 109 | git clean -dffx 110 | 111 | if [ -n "$RELEASE" ]; then 112 | echo "Checkout release '$RELEASE'" 113 | git checkout -f "$RELEASE" 114 | git merge "$REMOTE/$RELEASE" 2>/dev/null || :; 115 | else 116 | RELEASE="$(git describe --always --tags)" 117 | fi 118 | } 119 | 120 | 121 | build_image() { 122 | if [ ! -n "$MODEL" ]; then 123 | echo "You need to specify some router model for building image." 124 | return 1 125 | fi 126 | 127 | echo "Build image for $MODEL ($RELEASE)" 128 | USE_SUDO=1 $BUILD_IMAGE -y -p "$MODEL" "$SRCDIR" | tee "$LOGFILE" 129 | } 130 | 131 | 132 | copy_image_to_volume() { 133 | echo "Release image $MODEL ($RELEASE)" 134 | IMAGE=$(cat "$LOGFILE" | grep -Pzo 'Your image can be found here:\n.*' | tail -n1 | cut -f2-) 135 | if [ -n "$IMAGE" ] && [ -f "$SRCDIR/$IMAGE" ]; then 136 | if [ ! -d "$VOLUME/$RELEASEDIR" ]; then 137 | mkdir -p "$VOLUME/$RELEASEDIR" 138 | fi 139 | 140 | echo Copy image to $RELEASEDIR/$(basename "$IMAGE") 141 | cp "$SRCDIR/$IMAGE" "$VOLUME/$RELEASEDIR" 142 | else 143 | echo "Error: it seems build failed. See $LOGFILE for more details." 144 | echo "Failed" 145 | return 1 146 | fi 147 | } 148 | 149 | 150 | do_compile() { 151 | provision 152 | checkout_sources 153 | build_image 154 | 155 | if [ -d "$VOLUME" ]; then 156 | copy_image_to_volume 157 | fi 158 | 159 | echo "Done" 160 | } 161 | 162 | 163 | # parse arguments 164 | while test $# -gt 0; do 165 | opt="$1" 166 | 167 | case "$opt" in 168 | -h|--help) 169 | display_help 170 | exit 0 171 | ;; 172 | -m|--model) 173 | MODEL="$2" 174 | shift 175 | ;; 176 | -r|--release) 177 | RELEASE="$2" 178 | shift 179 | ;; 180 | --) # End of all options 181 | break; 182 | ;; 183 | -*) 184 | echo "${SELFNAME}: unrecognized option '$1'" >&2 185 | echo "Try '${SELFNAME} --help' for more information." >&2 186 | exit 1 187 | ;; 188 | *) 189 | display_help >&2 190 | exit 1 191 | ;; 192 | esac 193 | 194 | if test $# -gt 0; then 195 | shift 196 | else 197 | echo "${SELFNAME}: option '${opt}' requires an argument" >&2 198 | echo "Try '${SELFNAME} --help' for more information." >&2 199 | exit 1 200 | fi 201 | done 202 | 203 | 204 | do_compile 205 | 206 | -------------------------------------------------------------------------------- /debian-build-image: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # vim: set autoindent smartindent ts=4 sw=4 sts=4 noet filetype=sh: 3 | ############################################################################### 4 | ## Based on instructions from: 5 | ## https://github.com/RMerl/asuswrt-merlin/wiki/Compiling-from-source-using-a-Debian-based-Linux-Distribution 6 | ## https://github.com/RMerl/asuswrt-merlin/wiki/Compile-Firmware-from-source-using-Ubuntu 7 | ############################################################################### 8 | [[ -n "$DEBUG" ]] && set -x 9 | pushd $(dirname $0) > /dev/null; CURRABSPATH=$(readlink -nf "$(pwd)"); popd > /dev/null 10 | [[ -t 1 ]] && { G="\e[1;32m"; R="\e[1;31m"; Z="\e[0m"; B="\e[1;34m"; W="\e[1;37m"; Y="\e[1;33m"; } 11 | TESTED_ON="tested on: ${W}Ubuntu 12.04 & 14.04${Z} and ${W}Debian 7.8${Z}" 12 | REPORT_BACK="${W}Please report back${Z} about successes and failures at (e.g. by opening a ticket)" 13 | TOOLCHAIN_BINARIES="mipsel-linux-addr2line mipsel-linux-ar mipsel-linux-as mipsel-linux-c++ mipsel-linux-cc mipsel-linux-c++filt mipsel-linux-cpp mipsel-linux-g++ mipsel-linux-gcc mipsel-linux-gcc-4.2.4 mipsel-linux-gcov mipsel-linux-gprof mipsel-linux-ld mipsel-linux-nm mipsel-linux-objcopy mipsel-linux-objdump mipsel-linux-ranlib mipsel-linux-readelf mipsel-linux-size mipsel-linux-strings mipsel-linux-strip" 14 | INSTALL_PREREQ="" 15 | APTI="apt-get --no-install-recommends install" 16 | INTERACTIVE=1 17 | 18 | help_syntax() # show a brief help screen 19 | { 20 | echo -e "${W}SYNTAX:${Z} ${0##*/} [-p|--prereq] [-y|--yes] [-h|--help] [path]\n" 21 | echo -e "\twhere:\n\ 22 | \t* ${W}-p, --prereq${Z} Install all the prerequisites using 'apt-get'\n\ 23 | \t (please note that this requires you to be a sudoer)\n\ 24 | \t* ${W}-y, --yes${Z} Automatic yes to prompts\n\ 25 | \t (assume "yes" as answer to all prompts and run non-interactively)\n\ 26 | \t* ${W}path${Z} is a directory containing the asuswrt-merlin sources\n\ 27 | \t (default is the directory with this script: $CURRABSPATH)\n\ 28 | \t* ${W}router-model${Z} is the model name you'd pass to make\n\ 29 | \t (e.g. rt-n66u, rt-ac66u etc, see ${W}README.md${Z})\n" 30 | echo -e "Variables:\n\ 31 | \t${W}USE_SUDO${Z} if set will 'install' and use tools in /opt, but requires ${W}sudo${Z}.\n\ 32 | \t${W}FORCE_UNSUPPORTED${Z} if set will proceed even if the system doesn't match prerequisites 100%.\n\ 33 | \t${W}TMUX_PANE${Z} if unset/empty will suppress logging inside a Tmux session." 34 | } 35 | 36 | # parse arguments 37 | while [[ $# > 0 ]]; do 38 | case "$1" in 39 | -p|--prereq) 40 | INSTALL_PREREQ=1 41 | shift 42 | ;; 43 | -y|--yes) 44 | INTERACTIVE="" 45 | shift 46 | ;; 47 | -h|--help) 48 | help_syntax 49 | exit 0 50 | ;; 51 | -*) echo "unknown option: $1" >&2; 52 | exit 1 53 | ;; 54 | *) 55 | ROUTERMDL="${1}" 56 | BASEDIR="${2:-$CURRABSPATH}" 57 | shift; shift 58 | ;; 59 | esac 60 | done 61 | 62 | if [[ ! -n "$INTERACTIVE" ]]; then 63 | APTI="$APTI -y" 64 | export DEBIAN_FRONTEND=noninteractive 65 | fi 66 | 67 | let TIME_START=$(date +%s) 68 | CURRDATE=$(date +'%Y-%m-%dT%H-%M-%S') 69 | _TMUX_LOG="$BASEDIR/tmux-${CURRDATE}.log" 70 | 71 | show_time_diff() 72 | { 73 | local START=$1 74 | local END=$2 75 | local MSG=$3 76 | [[ -n "$MSG" ]] || MSG="Runtime: %s" 77 | local DIFF=$((END-START)) 78 | local DIFF_MIN=$((DIFF/60)) 79 | local DIFF_SEC=$((DIFF%60)) 80 | printf "$MSG\n" $(printf "%d:%02d" "$DIFF_MIN" "$DIFF_SEC") 81 | } 82 | fatal() # show the given error, include the help screen if requested and then exit with status 1 83 | { 84 | [[ "x$1" == "xhelp" ]] && { SHOWHELP=1; shift; } 85 | echo -e "${R}ERROR:${Z} ${1:-}" 86 | ((SHOWHELP)) && help_syntax 87 | exit 1 88 | } 89 | cleanup() # turn off tmux logging if enabled before, any other common cleanup tasks will be added here 90 | { 91 | if [[ -n "$TMUX_PANE" ]]; then 92 | echo -e "\n${Y}CLEANUP${Z}: turning off tmux logging ($_TMUX_LOG)" 93 | tmux pipe-pane -t $TMUX_PANE 94 | if [[ -f "$_TMUX_LOG" ]]; then 95 | sed -i.nocolor -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" "$_TMUX_LOG" && rm -f "${_TMUX_LOG}.nocolor" 96 | sed -i.bak -r 's#'"$BASEDIR"'#\$BASEDIR#g' "$_TMUX_LOG" && rm -f "${_TMUX_LOG}.bak" 97 | fi 98 | fi 99 | show_time_diff $TIME_START $(date +%s) 100 | } 101 | enable_tmux_logging() 102 | { 103 | if [[ -n "$TMUX_PANE" ]]; then 104 | echo -e "${Y}NOTE:${Z} turning on tmux logging ($_TMUX_LOG)" 105 | tmux pipe-pane -t $TMUX_PANE "cat >> $_TMUX_LOG" && trap cleanup EXIT 106 | fi 107 | } 108 | cleanup_and_revert() # reverts parts of the effects of this script, then calls cleanup() 109 | { 110 | echo -e "\n${Y}CLEANUP${Z}: extended cleanup of source tree changes" 111 | # Changed files will be reverted 112 | find "$BASEDIR" -type f -name '*.BAK-build-image'|while read fname; do 113 | ( set -x && mv "$fname" "${fname%%.BAK-build-image}" ) 114 | done 115 | # Simply remove proxy.h if we moved it there 116 | rm -f "$BASEDIR/release/src/router/neon/proxy.h" 117 | # Unlink the toolchain symlink, in case we were asked to put one 118 | [[ -n "$USE_SUDO" ]] && sudo unlink /opt/brcm 119 | cleanup 120 | } 121 | install_required_packages() 122 | { 123 | ((PREREQ_MISSING_NUM > 0)) || { echo -e "${Y}NOTE:${Z} no missing package dependencies were found."; return 0; } 124 | local REQ_PKGS="${REQ_PKGS[*]}" 125 | [[ -n "$1" ]] && REQ_PKGS="$1" 126 | echo -e "${W}INSTALLATION OF PREREQUISITE PACKAGES" 127 | echo -e "=====================================${Z}" 128 | [[ $UID -eq 0 ]] || { echo -e "${Y}NOTE:${Z} This requires ${W}sudo${Z}, unless you simulate the installation only."; SUDO=sudo; } 129 | if [[ "$(dpkg --print-architecture)" == "amd64" ]]; then 130 | if [[ $(dpkg --print-foreign-architectures) =~ i386 ]]; then 131 | DPKG_ADD_ARCH="true" 132 | else 133 | DPKG_ADD_ARCH="dpkg --add-architecture i386" 134 | echo -e "${Y}NOTE:${Z} you are running a $(dpkg --print-architecture) system, but we need libelf1:i386. So we will need to run\n\t${W}${SUDO+$SUDO }${DPKG_ADD_ARCH}${Z}\n... prior to the ${W}apt-get${Z} invocation. This is just to inform you about the fact.\n" 135 | fi 136 | else 137 | DPKG_ADD_ARCH="true" 138 | fi 139 | echo -e "The command to be run is (beware of line breaks in overlong lines!):\n\n${W}${SUDO+$SUDO }$APTI ${Z}${REQ_PKGS}\n" 140 | if [[ -n $INTERACTIVE ]]; then 141 | echo -ne "Do you want to continue? ([${W}y${Z}]es/[${W}N${Z}]o/[${W}s${Z}]imulate)" 142 | read -p " " ans 143 | [[ $ans =~ ^[YySs]$ ]] || { echo -e "\n${Y}NOTE:${Z} aborted by user."; exit 1; } 144 | else 145 | ans=y 146 | fi 147 | if [[ $ans =~ ^[Ss]$ ]]; then 148 | ( set -x; ${DPKG_ADD_ARCH:-true} && $APTI -s ${REQ_PKGS} ) 149 | exit 0 150 | else 151 | ( set -x; $SUDO $SHELL -c "$DPKG_ADD_ARCH && apt-get update && $APTI ${REQ_PKGS}" ) || fatal "an error occured when trying to install missing package dependencies." 152 | PREREQ_MISSING_NUM=0 153 | fi 154 | } 155 | check_or_fail_os_prereq() 156 | { 157 | [[ -z "$FORCE_UNSUPPORTED" ]] && fatal "this script has only been ${TESTED_ON}. However, you're running ${W}$(lsb_release -si) $(lsb_release -sr) ($(lsb_release -sc))${Z}. If you'd like to try it regardless, make sure to set ${W}FORCE_UNSUPPORTED=1${Z} prior to invoking it." 158 | } 159 | check_runtime_requirements() 160 | { 161 | # Does this look like a Debian? 162 | [[ -e "/etc/debian_version" ]] || fatal "this script expects a Debian flavor. It has been ${TESTED_ON}." 163 | [[ -n $(type -t lsb_release) ]] || fatal "${W}lsb_release{$Z} is expected to be installed. That is normally the case on Debian and Ubuntu anyway." 164 | OS_REL=$(lsb_release -sr|cut -d . -f 1) 165 | # All those packages are required according to the asuswrt-merlin docs (exceptions: I replaced g++ and make by build-essential, libncurses-dev => libncurses5-dev) 166 | PREREQ_PKGS=(autoconf automake bash bison build-essential bzip2 diffutils file flex gawk gcc-multilib gettext gperf groff-base libncurses5-dev libexpat1-dev libslang2 libssl-dev libtool libxml-parser-perl patch perl pkg-config python sed shtool tar texinfo unzip zlib1g zlib1g-dev) 167 | # Figure out the Debian flavor 168 | case $(lsb_release -si) in 169 | Debian) 170 | ((OS_REL < 7)) && check_or_fail_os_prereq 171 | ;; 172 | LinuxMint) 173 | echo -e "${Y}WARNING:${Z} this script should for all practical purposes run on Linux Mint >= 13 (maya), but it's possible that it doesn't. ${REPORT_BACK}." 174 | ((OS_REL < 13)) && check_or_fail_os_prereq 175 | ((OS_REL >= 16)) && { PREREQ_PKGS+=(automake1.11 autopoint intltool libproxy-dev); UBUNTU_EXTRAS=1; } 176 | ;; 177 | Ubuntu) 178 | ((OS_REL < 12)) && check_or_fail_os_prereq 179 | local UBUREL=0x$(lsb_release -sr|tr -d '.') 180 | # On Ubuntu 13.10 and later we check for two additional packages 181 | ((UBUREL >= 0x1310)) && { PREREQ_PKGS+=(automake1.11 autopoint intltool libproxy-dev); UBUNTU_EXTRAS=1; } 182 | ;; 183 | *) 184 | echo -e "${Y}WARNING:${Z} this is an ${W}unsupported Debian flavor${Z}. It is possible, even likely, that this will fail. ${REPORT_BACK}." 185 | ;; 186 | esac 187 | # On x86_64 we also need two additional packages 188 | [[ "$(uname -m)" == "x86_64" ]] && PREREQ_PKGS+=(lib32z1-dev lib32stdc++6) 189 | # For ARM routers add libelf1 190 | if [[ "$ARCH" == "ARM" ]]; then 191 | [[ "$(dpkg --print-architecture)" == "amd64" ]] && PREREQ_PKGS+=(libelf1:i386 libxml2-dev) || PREREQ_PKGS+=(libelf1 libxml2-dev) 192 | fi 193 | } 194 | tell_missing_packages() 195 | { 196 | local OTHER_ARCH_PREREQ_PKGS=() 197 | if [[ ${PREREQ_PKGS[*]} =~ :i386 ]] && [[ "$(dpkg --print-architecture)" == "amd64" ]]; then 198 | if [[ ! $(dpkg --print-foreign-architectures) =~ i386 ]]; then 199 | echo -e "${Y}NOTE:${Z} trying to find the list of missing packages may fail or yield an incomplete list.\n The reason is that you require a package from a foreign architecture not installed on your system: i386.\n You can still run this script with the ${W}--prereq${Z} option to fix missing dependencies." 200 | NEW_PREREQ_PKGS=() 201 | # Weed out packages which would cause errors 202 | for r in ${PREREQ_PKGS[*]}; do 203 | [[ $r =~ :i386 ]] && OTHER_ARCH_PREREQ_PKGS+=($r) || NEW_PREREQ_PKGS+=($r) 204 | done 205 | PREREQ_PKGS=(${NEW_PREREQ_PKGS[*]}) 206 | fi 207 | fi 208 | # Find the packages which are already installed, so we can remove them from the list of required packages 209 | local INST_ALREADY=($($APTI -s ${PREREQ_PKGS[*]}|awk '/is already the newest version\.$/ {print $1}')) 210 | PREREQ_MISSING=($(for pkg in $(echo ""|awk -f <(cat - <<-'EOF' 211 | # Embedded awk script 212 | BEGIN { 213 | EOF 214 | for r in ${PREREQ_PKGS[*]}; do echo " REQD[\"$r\"] = 0;"; done 215 | for i in ${INST_ALREADY[*]}; do echo " INST[\"$i\"] = 0;"; done 216 | cat - <<-'EOF' 217 | } 218 | END { 219 | for (i in INST) 220 | delete REQD[i] 221 | for (r in REQD) 222 | printf("%s\n", r) 223 | } 224 | EOF 225 | )); do echo "$pkg"; done|sort -u)) 226 | [[ ${#OTHER_ARCH_PREREQ_PKGS[@]} -gt 0 ]] && PREREQ_MISSING+=(${OTHER_ARCH_PREREQ_PKGS[*]}) 227 | PREREQ_MISSING_NUM=${#PREREQ_MISSING[*]} 228 | if ((PREREQ_MISSING_NUM > 0)); then 229 | echo -e "${R}ERROR:${Z} ${PREREQ_MISSING_NUM} required packages are missing on your system:\n\t${Z} ${PREREQ_MISSING[*]}\n" 230 | fi 231 | } 232 | ## HOOK INIT 233 | [[ -e "${CURRABSPATH%/}/.${0##*/}rc" ]] && { source "${CURRABSPATH%/}/.${0##*/}rc" || fatal "'${CURRABSPATH%/}/.${0##*/}rc' exists but could not be sourced successfully"; } 234 | function_exists() 235 | { 236 | declare -f $1 > /dev/null 2>&1 237 | # use exit status from declare 238 | } 239 | ## MAIN 240 | [[ -n "$ROUTERMDL" ]] || fatal help "you have to ${W}give the router model${Z} to build for." 241 | # Match the router model to find the correct base directory for make 242 | # To find supported router models: 243 | # grep -hoP 'RT-(AC|N)\d+[URW]*(_V\d+)?' release/src-rt*/target.mak|sort -u 244 | OLD_ROUTERMDL=${ROUTERMDL^^} 245 | case ${ROUTERMDL^^} in 246 | # release/src-rt 247 | RT-N16) 248 | MKBASE="src-rt";; 249 | # release/src-rt-6.x 250 | RT-AC66|RT-AC66[URW]) 251 | ROUTERMDL="RT-AC66U" 252 | MKBASE="src-rt-6.x";; 253 | RT-N66|RT-N66[URW]) 254 | ROUTERMDL="RT-N66U" 255 | MKBASE="src-rt-6.x";; 256 | # release/src-rt-6.x.4708 257 | RT-AC56|RT-AC56[URW]) 258 | ROUTERMDL="RT-AC56U" 259 | MKBASE="src-rt-6.x.4708";; 260 | RT-AC68|RT-AC68[URW]) 261 | ROUTERMDL="RT-AC68U" 262 | MKBASE="src-rt-6.x.4708";; 263 | RT-AC68_V2|RT-AC68[URW]_V2) 264 | ROUTERMDL="RT-AC68U_V2" 265 | MKBASE="src-rt-6.x.4708";; 266 | RT-AC69|RT-AC69[URW]) 267 | ROUTERMDL="RT-AC69U" 268 | MKBASE="src-rt-6.x.4708";; 269 | RT-AC87|RT-AC87[URW]) 270 | ROUTERMDL="RT-AC87U" 271 | MKBASE="src-rt-6.x.4708";; 272 | *) # anything unmatched 273 | if function_exists hook_unknown_router; then 274 | hook_unknown_router "${ROUTERMDL}" 275 | else 276 | fatal "unrecognized router model ${W}$ROUTERMDL${Z}. Either the command line or this script needs fixing [base]." 277 | fi;; 278 | esac 279 | case ${ROUTERMDL^^} in 280 | RT-N16|RT-AC66U|RT-N66U) 281 | ARCH="MIPS";; 282 | RT-AC56U|RT-AC68U|RT-AC68U_V2|RT-AC69U|RT-AC87U) 283 | ARCH="ARM";; 284 | *) 285 | if function_exists hook_unknown_router; then 286 | hook_unknown_router "${ROUTERMDL}" 287 | else 288 | fatal "unrecognized router model ${W}$ROUTERMDL${Z}. Either the command line or this script needs fixing [arch]." 289 | fi;; 290 | esac 291 | enable_tmux_logging 292 | function_exists hook_override_model && hook_override_model 293 | [[ "${OLD_ROUTERMDL^^}" == "${ROUTERMDL^^}" ]] || { echo -e "${W}INFO${Z}: your router model choice ${W}${OLD_ROUTERMDL^^}${Z} is firmware-compatible to ${ROUTERMDL^^}. Setting target for ${W}make${Z} to ${W}${ROUTERMDL^^}${Z}."; } 294 | unset OLD_ROUTERMDL 295 | # Check whether this is the asuswrt-merlin source directory ... 296 | [[ -f "$BASEDIR/README-merlin.txt" ]] || fatal help "$BASEDIR is expected to contain a file ${W}README-merlin.txt${Z}." 297 | check_runtime_requirements 298 | tell_missing_packages 299 | # Did the user ask to install prerequisites instead? 300 | [[ -n "$INSTALL_PREREQ" ]] && install_required_packages "${PREREQ_MISSING[*]}" 301 | ((PREREQ_MISSING_NUM > 0)) && fatal "can't continue because of missing packages.\n\nPlease use:\n\t${W}${0##*/} --prereq $ROUTERMDL${Z}\nor:\n\t${W}$APTI${Z} ${PREREQ_MISSING[*]}\nto install missing packages." 302 | if [[ -n "$INTERACTIVE" ]]; then 303 | if [[ -n "$INSTALL_PREREQ" ]]; then 304 | echo -ne "\nDo you want to proceed with the firmware build now? ([${W}y${Z}]es/[${W}N${Z}]o)" 305 | read -p " " ans 306 | [[ $ans =~ ^[Yy]$ ]] || { echo -e "\n${Y}INFO:${Z} aborted by user."; exit 0; } 307 | fi 308 | fi 309 | # Special steps required on Ubuntu newer than or equal to 13.10 310 | if [[ -n "$UBUNTU_EXTRAS" ]]; then 311 | echo -e "${Y}NOTE:${Z} Attempting extra fixes for Ubuntu >= 13.10 and similar" 312 | # fix neon missing proxy.h 313 | [[ -f "/usr/include/proxy.h" ]] || fatal "expected to find proxy.h from package libproxy-dev in /usr/include. It's not there, though." 314 | cp /usr/include/proxy.h "$BASEDIR/release/src/router/neon/" || fatal "failed to copy proxy.h into source tree" 315 | # fix broken configure script for libdaemon 316 | ( cd "$BASEDIR/release/src/router/libdaemon" && aclocal ) || fatal "failed to fix broken configure script for libdaemon" 317 | # fix broken configure script for libxml2 318 | ( cd "$BASEDIR/release/src/router/libxml2" && sed -i.BAK-build-image s/^AM_C_PROTOTYPES/#AM_C_PROTOTYPES/g configure.in && aclocal ) || fatal "failed to fix broken configure script for libxml2" 319 | function_exists hook_ubuntu_extras && hook_ubuntu_extras 320 | fi 321 | # Clean up leftovers 322 | echo -e "${Y}NOTE:${Z} removing excess files" 323 | rm -f "$BASEDIR/release/src/router/".#preconfigure* 324 | # Prepare the PATH environment variable and fix up hardcoded paths contained in certain files) 325 | [[ "$ARCH" == "ARM" ]] && USE_SUDO='' # no global paths needed, so no symlinking needed 326 | function_exists hook_pre_fixups && hook_pre_fixups 327 | if [[ -n "$USE_SUDO" ]]; then 328 | echo -e "${Y}NOTE:${Z} making toolchain available in /opt" 329 | sudo ln -sf "$BASEDIR/tools/brcm" /opt/brcm 330 | export TOOLCHAIN="/opt/brcm/hndtools-mipsel-uclibc" 331 | else 332 | # Cheapo version of "installing" the toolchain to /opt/brcm 333 | case "$ARCH" in 334 | ARM) 335 | # Instead of installing, we override some of the paths used in the build process 336 | export TOOLCHAIN="$BASEDIR/release/$MKBASE/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3" # used in preconfigure scripts 337 | ;; 338 | MIPS) 339 | echo -e "${Y}NOTE:${Z} looking for files in which to fix hardcoded path to toolchain" 340 | grep -P '=\s*/opt/brcm/hndtools-mipsel-uclibc/' $(find "$BASEDIR/release/" -type f -iname '*makefile' -o -name defconfig) 2>/dev/null|cut -d : -f 1|sort -u|while read fname; do 341 | [[ -e "${fname}.BAK-build-image" ]] && { echo -e "${W}INFO:${Z} skipping fixup of ${fname} (prior run detected)"; continue; } 342 | [[ "x${fname//.BAK-build-image/}" == "x$fname" ]] || continue # skip backup files 343 | echo -e "Rebasing paths in: ${G}${fname}${Z}" 344 | sed -i.BAK-build-image 's#/opt/brcm/hndtools-mipsel-uclibc/#'"$BASEDIR/tools/brcm/hndtools-mipsel-uclibc/"'#g' "$fname" && grep --color=auto "$BASEDIR/tools/brcm/hndtools-mipsel-uclibc/" "$fname" 345 | done 346 | # Instead of installing, we override some of the paths used in the build process 347 | export TOOLCHAIN="$BASEDIR/tools/brcm/hndtools-mipsel-uclibc" # used in preconfigure scripts 348 | ;; 349 | esac 350 | # Set the trap to also revert the changes we made 351 | trap cleanup_and_revert EXIT 352 | fi 353 | function_exists hook_post_fixups && hook_post_fixups 354 | # Add the toolchain into the PATH 355 | export PATH="$PATH:$TOOLCHAIN/bin" 356 | # Check that the relevant tools execute from there 357 | echo -e "${Y}NOTE:${Z} invoking each of the required programs to detect possible issues" 358 | for i in $TOOLCHAIN_BINARIES; do 359 | $i --version > /dev/null 2>&1 || \ 360 | { function_exists hook_suppress_missing_bin && hook_suppress_missing_bin "$i" || true; [[ $? -eq 0 ]] || fatal "failed to execute $i"; } 361 | done 362 | # Do the make 363 | function_exists hook_pre_clean && hook_pre_clean "$BASEDIR/release/$MKBASE" 364 | echo -e "${Y}NOTE:${Z} running ${W}make -C release/$MKBASE clean${Z}" 365 | make -C "$BASEDIR/release/$MKBASE" "TOOLCHAIN=$TOOLCHAIN" clean > /dev/null 2>&1 366 | function_exists hook_pre_make && hook_pre_make "$BASEDIR/release/$MKBASE" 367 | set -e 368 | echo -e "${Y}NOTE:${Z} running ${W}make -C release/$MKBASE ${ROUTERMDL,,}${Z}" 369 | make -C "$BASEDIR/release/$MKBASE" "TOOLCHAIN=$TOOLCHAIN" ${ROUTERMDL,,} 370 | function_exists hook_post_make && hook_post_make "$BASEDIR/release/$MKBASE" 371 | echo "Your image can be found here:" 372 | (cd "$BASEDIR" && for i in release/$MKBASE/image/*.trx; do echo -e "\t${G}$i${Z}"; done) 373 | -------------------------------------------------------------------------------- /mint-build-image: -------------------------------------------------------------------------------- 1 | debian-build-image -------------------------------------------------------------------------------- /ubuntu-build-image: -------------------------------------------------------------------------------- 1 | debian-build-image --------------------------------------------------------------------------------