new-build: get rid of Go(ogle) with few rc scripts

Also, in binutils: fix inlining compilation error,
see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=242053
This commit is contained in:
Giacomo Tesio 2021-10-27 10:11:43 +02:00
parent 6b473cd28f
commit 0c2dabf975
50 changed files with 278 additions and 3978 deletions

7
.gitignore vendored
View File

@ -1,6 +1,3 @@
third_party/pkg/
third_party/src/github.com/kr/
third_party/src/github.com/creack/
third_party/src/golang.org/
*.build.log
lib/

19
.gitmodules vendored
View File

@ -1,13 +1,18 @@
[submodule "third_party/src/github.com/lionkov/ninep"]
path = third_party/src/github.com/lionkov/ninep
url = https://github.com/JehanneOS/devtools-ninep.git
branch = master
[submodule "third_party/src/github.com/0intro/drawterm"]
path = third_party/src/github.com/0intro/drawterm
url = https://github.com/JehanneOS/devtools-drawterm.git
[submodule "cross/pkgs/newlib/src"]
path = cross/pkgs/newlib/src
url = https://github.com/JehanneOS/newlib.git
[submodule "cross/pkgs/mksh"]
path = cross/pkgs/mksh/src
url = https://github.com/JehanneOS/mksh.git
[submodule "src/trampoline"]
path = src/trampoline
url = https://tesio@bitbucket.org/tesio/trampoline.git
[submodule "src/u9fs"]
path = src/u9fs
url = https://bitbucket.org/plan9-from-bell-labs/u9fs.git
[submodule "src/drawterm"]
path = src/drawterm
url = https://github.com/JehanneOS/devtools-drawterm.git
[submodule "src/netcat"]
path = src/netcat
url = https://git.code.sf.net/p/nc110/git

4
QA.sh
View File

@ -11,7 +11,7 @@ fi
trap : 2
$JEHANNE/hacking/bin/ufs -d=0 -root=$JEHANNE &
$JEHANNE/hacking/bin/nc -p 5640 -l -e $JEHANNE/hacking/bin/start-u9fs.sh &
ufspid=$!
export machineflag=pc
@ -76,6 +76,6 @@ EOF
echo $cmd
eval $cmd
kill $ufspid
# kill $ufspid # not needed anymore thank to netcat
wait

1
bin/.gitignore vendored
View File

@ -1,2 +1,3 @@
*
!.gitignore
!build

3
bin/build Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
rc $JEHANNE/arch/rc/cmd/build $@

57
bin/runqemu Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env rc
# poor man portable expect
QPROMPT='10.0.2.15# '
INFILE=/tmp/runqemu.$pid.in
OUTFILE=/tmp/runqemu.$pid.out
cd $JEHANNE/arch/amd64/kern
echo -n > $INFILE
echo -n > $OUTFILE
@{tail -f $INFILE | $JEHANNE/hacking/QA.sh >> $OUTFILE} &
QPID=$apid
tail -f $OUTFILE &
TPID=$apid
ifs='
'
INPUT=`{cat}
fn descendants {
if( ! ~ 0 $#1 ) {
echo $1;
ps -o pid= --ppid $1|xargs -n 1 rc -c 'descendants $1'
}
}
fn quit {
kill -2 `{descendants $QPID}
kill -2 $TPID
wait
rm $INFILE
rm $OUTFILE
exit $1
}
fn failOnError {
if( ! ~ 0 `{cat $OUTFILE | grep FAIL | wc -c}){
quit 1
}
}
for(cmd in $INPUT){
while( ~ 0 `{tail -n 1 $OUTFILE | sed -n '/'$QPROMPT'/p' | wc -c}){
sleep 2
}
failOnError
echo $cmd >> $INFILE
sleep 2
}
while( ~ 0 `{tail -n 1 $OUTFILE | sed -n '/'$QPROMPT'/p' | wc -c}){
sleep 2
}
failOnError
quit

3
bin/start-u9fs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
# U9FS_LOG="-l $JEHANNE/hacking/lib/u9fs.log -z -D"
$JEHANNE/hacking/bin/u9fs $U9FS_LOG -a none -u $USER $JEHANNE

3
bin/template Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
$JEHANNE/hacking/bin/rc $JEHANNE/arch/rc/cmd/template $@

View File

@ -4,16 +4,16 @@
#
# Copyright (C) 2016-2017 Giacomo Tesio <giacomo@tesio.it>
BUILD_GO_TOOLS=true
BUILD_TRAMPOLINE=true
BUILD_DRAWTERM=true
while test $# -gt 0
do
case "$1" in
--help) echo "$0 [ --no-tools | --no-drawterm | --help ]"
--help) echo "$0 [ --no-trampoline | --no-drawterm | --help ]"
exit 0
;;
--no-tools) BUILD_GO_TOOLS=false
--no-tools) BUILD_TRAMPOLINE=false
;;
--no-drawterm) BUILD_DRAWTERM=false
;;
@ -29,10 +29,10 @@ cd `dirname $0`
if [ -z "$UTILITIES" ]; then
UTILITIES=`pwd`
fi
if [ "$BUILD_GO_TOOLS$BUILD_DRAWTERM" = "truetrue" ]; then
if [ "$BUILD_TRAMPOLINE$BUILD_DRAWTERM" = "truetrue" ]; then
git clean -x -d -f $UTILITIES/bin
fi
if [ "$BUILD_GO_TOOLS" = "true" ]; then
if [ "$BUILD_TRAMPOLINE" = "true" ]; then
echo -n Building development tools.
(
# Inside parentheses, and therefore a subshell . . .
@ -44,18 +44,20 @@ if [ "$BUILD_GO_TOOLS" = "true" ]; then
) &
dotter=$!
(
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go get -d jehanne/cmd/... &&
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go install jehanne/cmd/... &&
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go install github.com/lionkov/ninep/srv/examples/ufs
)
(cd $UTILITIES/src/trampoline/ && ./build.sh)
(cd $UTILITIES/src/netcat && XLIB=-lresolv DFLAGS=-DGAPING_SECURITY_HOLE make linux && mv nc $UTILITIES/bin && git clean -xdf .)
(cd $UTILITIES/src/u9fs && make && mv u9fs $UTILITIES/bin && git clean -xdf .)
) > $UTILITIES/src/utils.build.log 2>&1
STATUS="$?"
kill $dotter
wait $dotter 2>/dev/null
if [ ! $STATUS -eq "0" ]
then
echo "FAIL"
cat utils.build.log
exit $STATUS
else
rm $UTILITIES/src/utils.build.log
echo "done."
fi
fi
@ -72,7 +74,7 @@ if [ "$BUILD_DRAWTERM" = "true" ]; then
) &
dotter=$!
(
cd $UTILITIES/third_party/src/github.com/0intro/drawterm/ &&
cd $UTILITIES/src/drawterm/ &&
git clean -xdf > ../drawterm.build.log 2>&1 &&
CONF=unix make >> ../drawterm.build.log 2>&1 &&
mv drawterm $UTILITIES/bin
@ -82,11 +84,11 @@ if [ "$BUILD_DRAWTERM" = "true" ]; then
wait $dotter 2>/dev/null
if [ $STATUS -eq "0" ]
then
rm $UTILITIES/third_party/src/github.com/0intro/drawterm.build.log
rm $UTILITIES/src/drawterm.build.log
echo "done."
else
echo "FAIL"
cat $UTILITIES/third_party/src/github.com/0intro/drawterm.build.log
cat $$UTILITIES/src/drawterm.build.log
exit $STATUS
fi
fi

View File

@ -54,16 +54,7 @@ if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then
export TOOLPREFIX=x86_64-jehanne-
export CC=x86_64-jehanne-gcc
echo
echo "Vendorized code verification..."
echo
for v in `find $JEHANNE -type f|grep vendor.json`; do
echo "cd `dirname $v`"
(cd `dirname $v`; vendor -check)
done
echo
build all
build
if [ "$TRAVIS_BUILD_DIR" != "" ]; then
if [ "$QA_CHECKS" != "" ]; then

View File

@ -51,7 +51,7 @@ function dynpatch {
# setup Jehanne's headers
usyscalls header $JEHANNE/sys/src/sysconf.json > $JEHANNE/arch/amd64/include/syscalls.h
cat $JEHANNE/sys/src/lib/jehanne/syscallh.rc.template | template | rc | sed 's/ ,/,/g; s/ )/)/g' > $JEHANNE/arch/amd64/include/syscalls.h
mkdir -p $WORKING_DIR
date > $LOG
@ -63,7 +63,7 @@ failOnError $? "libtool installation check"
cp -fpr $JEHANNE/hacking/cross/src $WORKING_DIR
cd $WORKING_DIR/src
fetch >> $LOG
./fetch.sh >> $LOG
failOnError $? "fetching sources"
mkdir -p $WORKING_DIR/build
@ -76,10 +76,12 @@ echo -n Building binutils...
export BINUTILS_BUILD_DIR=$WORKING_DIR/build/binutils
mkdir -p $BINUTILS_BUILD_DIR
# for libctf/swap.h patch see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=242053
( ( grep -q jehanne $WORKING_DIR/src/binutils/config.sub || (
cd $WORKING_DIR &&
sed -i '/jehanne/b; /ELF_TARGET_ID/,/elf_backend_can_gc_sections/s/0x200000/0x1000 \/\/ jehanne hack/g' src/binutils/bfd/elf64-x86-64.c &&
sed -i '/jehanne/b; s/| midnightbsd\*/| midnightbsd* | jehanne*/g' src/binutils/config.sub &&
sed -i 's/inline/static inline/g' src/binutils/libctf/swap.h &&
dynpatch 'binutils/bfd/config.bfd' '\# END OF targmatch.h' &&
dynpatch 'binutils/gas/configure.tgt' ' i386-\*-darwin\*)' &&
( grep -q jehanne src/binutils/ld/configure.tgt || patch -p1 < $CROSS_DIR/patch/binutils/ld/configure.tgt ) &&

View File

@ -1,12 +0,0 @@
{
"CrossCompiledPackages": {
"Env": [
"CROSS_PKGS_BUILD=1"
],
"Pre": [
"./newlib/build.sh",
"./mksh/build.sh",
"./gcc/build.sh"
]
}
}

10
cross/pkgs/build.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
export IFS="$ifs"
export CROSS_PKGS_BUILD=1
export CFLAGS=''
export CC=''
./newlib/build.sh
./mksh/build.sh
./gcc/build.sh

View File

@ -50,7 +50,7 @@ ln -s `which echo` $JEHANNE/hacking/bin/makeinfo
libtool --version >> /dev/null
failOnError $? "libtool installation check"
(cd $JEHANNE_TOOLCHAIN/src && fetch) >> $LOG
(cd $JEHANNE_TOOLCHAIN/src && ./fetch.sh) >> $LOG
failOnError $? "fetching sources"

45
cross/pkgs/gcc/src/fetch.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh -e
download () {
TARGET=$(basename $1)
if [ -f $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
elif command -v curl >/dev/null 2>&1; then
curl -s --fail "$1" > $TARGET
elif command -v wget >/dev/null 2>&1; then
wget -O- "$1" > $TARGET
else
echo "ERROR: Unable to find either curl or wget" >&2
exit 1
fi
}
extract () {
TARGET=$1
ARCHIVE=$2
if [ -d $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
else
mkdir -p $TARGET
(cd $TARGET && tar xf ../$ARCHIVE --strip-components=1)
fi
}
echo "fetch: downloading..."
download 'https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2'
download 'https://www.mpfr.org/mpfr-4.0.1/mpfr-4.0.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz'
download 'http://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.gz'
echo "fetch: check sum..."
sha256sum -c sha256sum.txt
echo "fetch: extract archive..."
extract libgmp gmp-6.1.2.tar.bz2
extract libmpfr mpfr-4.0.1.tar.bz2
extract libmpc mpc-1.1.0.tar.gz
extract binutils binutils-2.33.1.tar.bz2
extract gcc gcc-9.2.0.tar.gz
echo "fetch: done."

View File

@ -0,0 +1,5 @@
0cb4843da15a65a953907c96bad658283f3c4419d6bcc56bf2789db16306adb2 binutils-2.33.1.tar.bz2
a931a750d6feadacbeecb321d73925cd5ebb6dfa7eff0802984af3aef63759f4 gcc-9.2.0.tar.gz
5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2 gmp-6.1.2.tar.bz2
6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e mpc-1.1.0.tar.gz
a4d97610ba8579d380b384b225187c250ef88cfe1d5e7226b89519374209b86b mpfr-4.0.1.tar.bz2

View File

@ -1,53 +0,0 @@
{
"libgmp" : {
"Upstream":"https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2",
"Digest": {
"sha256":"5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2"
},
"Compress":"bzip2",
"RemovePrefix": true,
"Exclude": [
]
},
"libmpfr" : {
"Upstream":"https://www.mpfr.org/mpfr-4.0.1/mpfr-4.0.1.tar.bz2",
"Digest": {
"sha256":"a4d97610ba8579d380b384b225187c250ef88cfe1d5e7226b89519374209b86b"
},
"Compress":"bzip2",
"RemovePrefix": true,
"Exclude": [
]
},
"libmpc" : {
"Upstream":"https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz",
"Digest": {
"sha256":"6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e"
},
"Compress":"gzip",
"RemovePrefix": true,
"Exclude": [
]
},
"binutils": {
"Upstream":"http://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2",
"Digest": {
"sha256":"0cb4843da15a65a953907c96bad658283f3c4419d6bcc56bf2789db16306adb2"
},
"Compress":"bzip2",
"RemovePrefix": true,
"Exclude": [
]
},
"gcc": {
"Upstream":"https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.gz",
"Digest": {
"sha256":"a931a750d6feadacbeecb321d73925cd5ebb6dfa7eff0802984af3aef63759f4"
},
"Compress":"gzip",
"RemovePrefix": true,
"Exclude": [
]
}
}

45
cross/src/fetch.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh -e
download () {
TARGET=$(basename $1)
if [ -f $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
elif command -v curl >/dev/null 2>&1; then
curl -s --fail "$1" > $TARGET
elif command -v wget >/dev/null 2>&1; then
wget -O- "$1" > $TARGET
else
echo "ERROR: Unable to find either curl or wget" >&2
exit 1
fi
}
extract () {
TARGET=$1
ARCHIVE=$2
if [ -d $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
else
mkdir -p $TARGET
(cd $TARGET && tar xf ../$ARCHIVE --strip-components=1)
fi
}
echo "fetch: downloading..."
download 'https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2'
download 'https://www.mpfr.org/mpfr-4.0.1/mpfr-4.0.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz'
download 'http://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.gz'
echo "fetch: check sum..."
sha256sum -c sha256sum.txt
echo "fetch: extract archive..."
extract libgmp gmp-6.1.2.tar.bz2
extract libmpfr mpfr-4.0.1.tar.bz2
extract libmpc mpc-1.1.0.tar.gz
extract binutils binutils-2.33.1.tar.bz2
extract gcc gcc-9.2.0.tar.gz
echo "fetch: done."

5
cross/src/sha256sum.txt Normal file
View File

@ -0,0 +1,5 @@
0cb4843da15a65a953907c96bad658283f3c4419d6bcc56bf2789db16306adb2 binutils-2.33.1.tar.bz2
a931a750d6feadacbeecb321d73925cd5ebb6dfa7eff0802984af3aef63759f4 gcc-9.2.0.tar.gz
5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2 gmp-6.1.2.tar.bz2
6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e mpc-1.1.0.tar.gz
a4d97610ba8579d380b384b225187c250ef88cfe1d5e7226b89519374209b86b mpfr-4.0.1.tar.bz2

View File

@ -9,10 +9,16 @@ REPONAME=`basename $JEHANNE`
JEHANNE_TOOLCHAIN=`dirname $JEHANNE`
JEHANNE_TOOLCHAIN="$JEHANNE_TOOLCHAIN/$REPONAME.TOOLCHAIN"
export JEHANNE_TOOLCHAIN
JEHANNE_TOOLCHAIN_CROSS="$JEHANNE_TOOLCHAIN/cross"
export JEHANNE_TOOLCHAIN_CROSS
JEHANNE_HACKING=$JEHANNE/hacking
export JEHANNE_HACKING
export PATH="$JEHANNE/hacking/bin:$PATH"
export PATH="$JEHANNE_TOOLCHAIN/cross/posix/bin:$PATH"
#export CPATH="$JEHANNE_TOOLCHAIN/cross/posix/lib/gcc/x86_64-jehanne/9.2.0/include:$JEHANNE_TOOLCHAIN/cross/posix/lib/gcc/x86_64-jehanne/9.2.0/include-fixed"
export PATH="$JEHANNE_HACKING/bin:$PATH"
export PATH_CLONE=$PATH
export ARCH=amd64
export TOOLPREFIX=x86_64-jehanne-

51
rcmain Normal file
View File

@ -0,0 +1,51 @@
# rcmain: Plan 9 on Unix version
if(~ $#home 0) home=$HOME
if(~ $#home 0) home=/
if(~ $#ifs 0) ifs='
'
switch($#prompt){
case 0
prompt=('% ' ' ')
case 1
prompt=($prompt ' ')
}
if(~ $rcname ?.out ?.rc */?.rc */?.out) prompt=('broken! ' ' ')
if(flag p) path=(/bin /usr/bin)
if not{
finit
# should be taken care of by rc now, but leave just in case
}
fn sigexit
if(! ~ $#cflag 0){
if(flag l && test -r $home/lib/profile) . $home/lib/profile
status=''
eval $cflag
exit $status
}
if(flag i){
if(~ $termprog 9term || ~ $termprog win){
fn cd {
# builtin cd $1 && flag i && awd
# is not sufficient when running in a subshell
# that is rc -e (like mk uses!)
if(builtin cd $1){
if(flag i) $PLAN9/bin/9 awd || status=''
status=''
}
}
$PLAN9/bin/9 awd
}
if(flag l && test -r $home/lib/profile) . $home/lib/profile
status=''
if(! ~ $#* 0) . $*
. -i '/dev/stdin'
exit $status
}
if(flag l && test -r $home/lib/profile) . $home/lib/profile
if(~ $#* 0){
. /dev/stdin
exit $status
}
status=''
. $*
exit $status

View File

@ -13,8 +13,7 @@ fi
trap : 2
$JEHANNE/hacking/bin/ufs -root=$JEHANNE &
# To debug ufs add: -d 5 > $JEHANNE/../ufs.log 2>&1
$JEHANNE/hacking/bin/nc -p 5640 -l -e $JEHANNE/hacking/bin/start-u9fs.sh &
ufspid=$!
export machineflag=pc
@ -53,7 +52,7 @@ QEMU_USER=`whoami`
cd $KERNDIR
read -r cmd <<EOF
$kvmdo qemu-system-x86_64 -s -cpu Haswell -smp $NCPU -m 2048 $kvmflag \
$kvmdo qemu-system-x86_64 -s -cpu max -smp $NCPU -m 2048 $kvmflag \
-rtc clock=vm \
-no-reboot -serial mon:stdio \
--machine $machineflag \
@ -74,5 +73,5 @@ EOF
echo $cmd
eval $cmd
kill $ufspid
# kill $ufspid # not needed anymore thank to netcat
wait

View File

@ -1,669 +0,0 @@
// Build builds code as directed by json files.
// We slurp in the JSON, and recursively process includes.
// At the end, we issue a single cc command for all the files.
// Compilers are fast.
//
// ENVIRONMENT
//
// Needed: JEHANNE, ARCH
//
// JEHANNE should point to a Jehanne root.
// Currently only "amd64" is a valid ARCH.
// A best-effort to autodetect the Jehanne root is made if not explicitly set.
//
// Optional: CC, AR, LD, RANLIB, STRIP, SH, TOOLPREFIX
//
// These all control how the needed tools are found.
//
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
)
type kernconfig struct {
Code []string
Dev []string
Ip []string
Link []string
Sd []string
Uart []string
VGA []string
}
type kernel struct {
CodeFile string
Systab string
Config kernconfig
Ramfiles map[string]string
}
type build struct {
// jsons is unexported so can not be set in a .json file
jsons map[string]bool
path string
name string
// Projects name a whole subproject which is built independently of
// this one. We'll need to be able to use environment variables at some point.
Projects []string
Pre []string
Post []string
Cflags []string
Oflags []string
Include []string
SourceFiles []string
ObjectFiles []string
Libs []string
Env []string
// cmd's
SourceFilesCmd []string
// Targets.
Program string
Library string
Install string // where to place the resulting binary/lib
Kernel *kernel
}
type buildfile map[string]build
// UnmarshalJSON works like the stdlib unmarshal would, except it adjusts all
// paths.
func (bf *buildfile) UnmarshalJSON(s []byte) error {
r := make(map[string]build)
if err := json.Unmarshal(s, &r); err != nil {
return err
}
for k, b := range r {
// we're getting a copy of the struct, remember.
b.jsons = make(map[string]bool)
b.Projects = adjust(b.Projects)
b.Libs = adjust(b.Libs)
b.Cflags = adjust(b.Cflags)
b.SourceFiles = b.SourceFiles
b.SourceFilesCmd = b.SourceFilesCmd
b.ObjectFiles = b.ObjectFiles
b.Include = adjust(b.Include)
b.Install = fromRoot(b.Install)
for i, e := range b.Env {
b.Env[i] = os.ExpandEnv(e)
}
r[k] = b
}
*bf = r
return nil
}
var (
cwd string
jehanne string
regexpAll = []*regexp.Regexp{regexp.MustCompile(".")}
// findTools looks at all env vars and absolutizes these paths
// also respects TOOLPREFIX
tools = map[string]string{
"cc": "gcc",
"ar": "ar",
"ld": "ld",
"ranlib": "ranlib",
"strip": "strip",
"sh": "sh",
}
arch = map[string]bool{
"amd64": true,
}
debugPrint = flag.Bool("debug", false, "enable debug prints")
shellhack = flag.Bool("shellhack", false, "spawn every command in a shell (forced on if LD_PRELOAD is set)")
)
func debug(fmt string, s ...interface{}) {
if *debugPrint {
log.Printf(fmt, s...)
}
}
// fail with message, if err is not nil
func failOn(err error) {
if err != nil {
log.Fatalf("%v\n", err)
}
}
func isValueInList(value string, list []string) bool {
for _, v := range list {
if v == value {
return true
}
}
return false
}
func adjust(s []string) []string {
for i, v := range s {
s[i] = fromRoot(v)
}
return s
}
func buildEnv(b *build) func(string) string{
return func(v string) string {
search := v + "="
for _, s := range b.Env {
if strings.Index(s, search) == 0 {
return strings.Replace(s, search, "", 1)
}
}
return os.Getenv(v)
}
}
// return the given absolute path as an absolute path rooted at the jehanne tree.
func fromRoot(p string) string {
p = os.ExpandEnv(p)
if path.IsAbs(p) {
return path.Join(jehanne, p)
}
return p
}
// Sh sends cmd to a shell. It's needed to enable $LD_PRELOAD tricks,
// see https://github.com/Harvey-OS/jehanne/issues/8#issuecomment-131235178
func sh(cmd *exec.Cmd) {
shell := exec.Command(tools["sh"])
shell.Env = cmd.Env
if cmd.Args[0] == tools["sh"] && cmd.Args[1] == "-c" {
cmd.Args = cmd.Args[2:]
}
commandString := strings.Join(cmd.Args, " ")
if shStdin, e := shell.StdinPipe(); e == nil {
go func() {
defer shStdin.Close()
io.WriteString(shStdin, commandString)
}()
} else {
log.Fatalf("cannot pipe [%v] to %s: %v", commandString, tools["sh"], e)
}
shell.Stderr = os.Stderr
shell.Stdout = os.Stdout
debug("%q | sh\n", commandString)
failOn(shell.Run())
}
func mergeKernel(k *kernel, defaults *kernel) *kernel {
if k == nil {
return defaults
}
if defaults == nil {
return k
}
// The custom kernel Code will be added after the default from includes
// so that it has a chance to change de default behaviour.
k.Config.Code = append(defaults.Config.Code, k.Config.Code...)
k.Config.Dev = append(k.Config.Dev, defaults.Config.Dev...)
k.Config.Ip = append(k.Config.Ip, defaults.Config.Ip...)
k.Config.Link = append(k.Config.Link, defaults.Config.Link...)
k.Config.Sd = append(k.Config.Sd, defaults.Config.Sd...)
k.Config.Uart = append(k.Config.Uart, defaults.Config.Uart...)
k.Config.VGA = append(k.Config.VGA, defaults.Config.VGA...)
if k.CodeFile == "" {
k.CodeFile = defaults.CodeFile
}
if k.Systab == "" {
k.Systab = defaults.Systab
}
for name, path := range defaults.Ramfiles {
if _, ok := k.Ramfiles[name]; ok == false {
k.Ramfiles[name] = path
}
}
return k
}
func include(f string, b *build) {
if b.jsons[f] {
return
}
b.jsons[f] = true
log.Printf("Including %v", f)
d, err := ioutil.ReadFile(f)
failOn(err)
var builds buildfile
failOn(json.Unmarshal(d, &builds))
for n, build := range builds {
log.Printf("Merging %v", n)
b.SourceFiles = append(b.SourceFiles, build.SourceFiles...)
b.Cflags = append(b.Cflags, build.Cflags...)
b.Oflags = append(b.Oflags, build.Oflags...)
b.Pre = append(b.Pre, build.Pre...)
b.Post = append(b.Post, build.Post...)
b.Libs = append(b.Libs, build.Libs...)
b.Projects = append(b.Projects, build.Projects...)
b.Env = append(b.Env, build.Env...)
b.SourceFilesCmd = append(b.SourceFilesCmd, build.SourceFilesCmd...)
b.Program += build.Program
b.Library += build.Library
b.Kernel = mergeKernel(b.Kernel, build.Kernel)
if build.Install != "" {
if b.Install != "" {
log.Fatalf("In file %s (target %s) included by %s (target %s): redefined Install.", f, n, build.path, build.name)
}
b.Install = build.Install
}
b.ObjectFiles = append(b.ObjectFiles, build.ObjectFiles...)
// For each source file, assume we create an object file with the last char replaced
// with 'o'. We can get smarter later.
for _, v := range build.SourceFiles {
f := path.Base(v)
o := f[:len(f)-1] + "o"
b.ObjectFiles = append(b.ObjectFiles, o)
}
for _, v := range build.Include {
if !path.IsAbs(v) {
wd := path.Dir(f)
v = path.Join(wd, v)
}
include(v, b)
}
}
}
func appendIfMissing(s []string, v string) []string {
for _, a := range s {
if a == v {
return s
}
}
return append(s, v)
}
func process(f string, r []*regexp.Regexp) []build {
log.Printf("Processing %v", f)
var builds buildfile
var results []build
d, err := ioutil.ReadFile(f)
failOn(err)
failOn(json.Unmarshal(d, &builds))
// Sort keys alphabetically (GoLang does not preserve the JSON order)
var keys []string
for n := range builds {
keys = append(keys, n)
}
sort.Strings(keys)
for _, n := range keys {
build := builds[n]
build.name = n
build.jsons = make(map[string]bool)
skip := true
for _, re := range r {
if re.MatchString(build.name) {
skip = false
break
}
}
if skip {
continue
}
log.Printf("Run %v", build.name)
build.jsons[f] = true
build.path = path.Dir(f)
// For each source file, assume we create an object file with the last char replaced
// with 'o'. We can get smarter later.
for _, v := range build.SourceFiles {
f := path.Base(v)
o := f[:len(f)-1] + "o"
build.ObjectFiles = appendIfMissing(build.ObjectFiles, o)
}
for _, v := range build.Include {
include(v, &build)
}
results = append(results, build)
}
return results
}
func buildkernel(b *build) {
if b.Kernel == nil {
return
}
envFunc := buildEnv(b)
for name, path := range b.Kernel.Ramfiles {
b.Kernel.Ramfiles[name] = os.Expand(path, envFunc);
}
codebuf := confcode(b.path, b.Kernel)
if b.Kernel.CodeFile == "" {
log.Fatalf("Missing Kernel.CodeFile in %v\n", b.path)
}
failOn(ioutil.WriteFile(b.Kernel.CodeFile, codebuf, 0666))
}
func wrapInQuote(args []string) []string {
var res []string
for _, a := range(args){
if strings.Contains(a, "=") {
res = append(res, "'" + a + "'")
} else {
res = append(res, a)
}
}
return res
}
func convertLibPathsToArgs(b *build) []string {
libLocations := make([]string, 0)
args := make([]string, 0)
defaultLibLocation := fromRoot("/arch/$ARCH/lib")
for _, lib := range b.Libs {
ldir := filepath.Dir(lib)
if ldir != defaultLibLocation {
if !isValueInList(ldir, libLocations) {
libLocations = append(libLocations, ldir)
args = append(args, "-L", ldir)
}
}
lib = strings.Replace(lib, ldir + "/lib", "-l", 1)
lib = strings.Replace(lib, ".a", "", 1)
args = append(args, lib)
}
return args
}
func compile(b *build) {
log.Printf("Building %s\n", b.name)
// N.B. Plan 9 has a very well defined include structure, just three things:
// /amd64/include, /sys/include, .
args := b.SourceFiles
args = append(args, b.Cflags...)
if !isValueInList("-c", b.Cflags) {
args = append(args, convertLibPathsToArgs(b)...)
args = append(args, b.Oflags...)
}
if len(b.SourceFilesCmd) > 0 {
for _, i := range b.SourceFilesCmd {
largs := make([]string, 3)
largs[0] = i
largs[1] = "-o"
largs[2] = strings.Replace(filepath.Base(i), filepath.Ext(i), "", 1)
cmd := exec.Command(tools["cc"], append(largs, args...)...)
run(b, *shellhack, cmd)
}
return
}
if !isValueInList("-c", b.Cflags) {
args = append(args, "-o", b.Program)
}
cmd := exec.Command(tools["cc"], args...)
run(b, *shellhack, cmd)
}
func link(b *build) {
if !isValueInList("-c", b.Cflags) {
return
}
log.Printf("Linking %s\n", b.name)
if len(b.SourceFilesCmd) > 0 {
for _, n := range b.SourceFilesCmd {
// Split off the last element of the file
var ext = filepath.Ext(n)
if len(ext) == 0 {
log.Fatalf("refusing to overwrite extension-less source file %v", n)
continue
}
n = n[:len(n)-len(ext)]
f := path.Base(n)
o := f[:len(f)] + ".o"
args := []string{"-o", n, o}
args = append(args, b.Oflags...)
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
return
}
args := []string{"-o", b.Program}
args = append(args, b.ObjectFiles...)
args = append(args, b.Oflags...)
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
func install(b *build) {
if b.Install == "" {
return
}
log.Printf("Installing %s\n", b.name)
failOn(os.MkdirAll(b.Install, 0755))
switch {
case len(b.SourceFilesCmd) > 0:
for _, n := range b.SourceFilesCmd {
ext := filepath.Ext(n)
exe := n[:len(n)-len(ext)]
move(exe, b.Install)
}
case len(b.Program) > 0:
move(b.Program, b.Install)
case len(b.Library) > 0:
ofiles := []string{}
for _, o := range b.ObjectFiles {
os.Rename(o, b.Library + "-" + o)
ofiles = append(ofiles, b.Library + "-" + o)
}
libpath := path.Join(b.Install, b.Library)
args := append([]string{"-rs", libpath}, ofiles...)
run(b, *shellhack, exec.Command(tools["ar"], args...))
run(b, *shellhack, exec.Command(tools["ranlib"], libpath))
}
}
func move(from, to string) {
final := path.Join(to, from)
log.Printf("move %s %s\n", from, final)
_ = os.Remove(final)
failOn(os.Link(from, final))
failOn(os.Remove(from))
}
func run(b *build, pipe bool, cmd *exec.Cmd) {
if b != nil {
cmd.Env = append(os.Environ(), b.Env...)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if pipe {
// Sh sends cmd to a shell. It's needed to enable $LD_PRELOAD tricks, see https://github.com/Harvey-OS/jehanne/issues/8#issuecomment-131235178
shell := exec.Command(tools["sh"])
shell.Env = cmd.Env
shell.Stderr = os.Stderr
shell.Stdout = os.Stdout
commandString := cmd.Args[0]
commandString += " " + strings.Join(wrapInQuote(cmd.Args[1:]), " ")
shStdin, err := shell.StdinPipe()
if err != nil {
log.Fatalf("cannot pipe [%v] to %s: %v", commandString, tools["sh"], err)
}
go func() {
defer shStdin.Close()
io.WriteString(shStdin, commandString)
}()
log.Printf("%q | %s\n", commandString, tools["sh"])
failOn(shell.Run())
return
}
log.Println(strings.Join(cmd.Args, " "))
failOn(cmd.Run())
}
func projects(b *build, r []*regexp.Regexp) {
for _, v := range b.Projects {
f, _ := findBuildfile(v)
log.Printf("Doing %s\n", f)
project(f, r, b)
}
}
// assumes we are in the wd of the project.
func project(bf string, which []*regexp.Regexp, container *build) {
cwd, err := os.Getwd()
failOn(err)
debug("Start new project cwd is %v", cwd)
defer os.Chdir(cwd)
dir := path.Dir(bf)
root := path.Base(bf)
debug("CD to %v and build using %v", dir, root)
failOn(os.Chdir(dir))
builds := process(root, which)
debug("Processing %v: %d target", root, len(builds))
for _, b := range builds {
debug("Processing %v: %v", b.name, b)
if container != nil {
b.Env = append(container.Env, b.Env...)
}
projects(&b, regexpAll)
for _, c := range b.Pre {
// this is a hack: we just pass the command through as an exec.Cmd
run(&b, true, exec.Command(c))
}
envFunc := buildEnv(&b);
b.Program = os.Expand(b.Program, envFunc)
for i, s := range b.SourceFiles {
b.SourceFiles[i] = fromRoot(os.Expand(s, envFunc));
}
for i, s := range b.SourceFilesCmd {
b.SourceFilesCmd[i] = fromRoot(os.Expand(s, envFunc));
}
for i, s := range b.ObjectFiles {
b.ObjectFiles[i] = fromRoot(os.Expand(s, envFunc));
}
buildkernel(&b)
if len(b.SourceFiles) > 0 || len(b.SourceFilesCmd) > 0 {
compile(&b)
}
if b.Program != "" || len(b.SourceFilesCmd) > 0 {
link(&b)
}
install(&b)
for _, c := range b.Post {
run(&b, true, exec.Command(c))
}
}
}
func main() {
// A small amount of setup is done in the paths*.go files. They are
// OS-specific path setup/manipulation. "jehanne" is set there and $PATH is
// adjusted.
var err error
findTools(os.Getenv("TOOLPREFIX"))
flag.Parse()
cwd, err = os.Getwd()
failOn(err)
a := os.Getenv("ARCH")
if a == "" || !arch[a] {
s := []string{}
for i := range arch {
s = append(s, i)
}
log.Fatalf("You need to set the ARCH environment variable from: %v", s)
}
// ensure this is exported, in case we used a default value
os.Setenv("JEHANNE", jehanne)
if os.Getenv("LD_PRELOAD") != "" {
log.Println("Using shellhack")
*shellhack = true
}
// If no args, assume 'build.json'
// Otherwise the first argument is either
// - the path to a json file
// - a directory containing a 'build.json' file
// - a regular expression to apply assuming 'build.json'
// Further arguments are regular expressions.
consumedArgs := 0;
bf := ""
if len(flag.Args()) == 0 {
f, err := findBuildfile("build.json")
failOn(err)
bf = f
} else {
f, err := findBuildfile(flag.Arg(0))
failOn(err)
if f == "" {
f, err := findBuildfile("build.json")
failOn(err)
bf = f
} else {
consumedArgs = 1
bf = f
}
}
re := []*regexp.Regexp{regexp.MustCompile(".")}
if len(flag.Args()) > consumedArgs {
re = re[:0]
for _, r := range flag.Args()[consumedArgs:] {
rx, err := regexp.Compile(r)
failOn(err)
re = append(re, rx)
}
}
project(bf, re, nil)
}
func findTools(toolprefix string) {
var err error
for k, v := range tools {
if x := os.Getenv(strings.ToUpper(k)); x != "" {
v = x
}
if toolprefix != "" && v != "sh" && !strings.Contains(v, toolprefix) {
v = toolprefix + v;
}
v, err = exec.LookPath(v)
failOn(err)
tools[k] = v
}
}
// disambiguate the buildfile argument
func findBuildfile(f string) (string, error) {
if strings.HasSuffix(f, ".json"){
if fi, err := os.Stat(f); err == nil && !fi.IsDir() {
return f, nil
}
return "", fmt.Errorf("unable to find buildfile %s", f)
}
if strings.Contains(f, "/") {
return findBuildfile(path.Join(f, "build.json"))
}
return "", nil
}

View File

@ -1,182 +0,0 @@
package main
import (
"bytes"
"debug/elf"
"fmt"
"io/ioutil"
"os"
"os/exec"
"text/template"
)
const kernconfTmpl = `
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
void
rdb(void)
{
splhi();
iprint("rdb...not installed\n");
for(;;);
}
{{ range .Rootcodes }}
{{ . }}
{{ end }}
{{ range .Config.Dev }}extern Dev {{ . }}devtab;
{{ end }}
Dev *devtab[] = {
{{ range .Config.Dev }}
&{{ . }}devtab,
{{ end }}
nil,
};
{{ range .Config.Link }}extern void {{ . }}link(void);
{{ end }}
void
links(void)
{
{{ range .Rootnames }}addbootfile("{{ . }}", ramfs_{{ . }}_code, ramfs_{{ . }}_len);
{{ end }}
{{ range .Config.Link }}{{ . }}link();
{{ end }}
}
#include "../ip/ip.h"
{{ range .Config.Ip }}extern void {{ . }}init(Fs*);
{{ end }}
void (*ipprotoinit[])(Fs*) = {
{{ range .Config.Ip }} {{ . }}init,
{{ end }}
nil,
};
#include "../port/sd.h"
{{ range .Config.Sd }}extern SDifc {{ . }}ifc;
{{ end }}
SDifc* sdifc[] = {
{{ range .Config.Sd }} &{{ . }}ifc,
{{ end }}
nil,
};
{{ range .Config.Uart }}extern PhysUart {{ . }}physuart;
{{ end }}
PhysUart* physuart[] = {
{{ range .Config.Uart }} &{{ . }}physuart,
{{ end }}
nil,
};
#define Image IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"
{{ range .Config.VGA }}extern VGAdev {{ . }}dev;
{{ end }}
VGAdev* vgadev[] = {
{{ range .Config.VGA }} &{{ . }}dev,
{{ end }}
nil,
};
{{ range .Config.VGA }}extern VGAcur {{ . }}cur;
{{ end }}
VGAcur* vgacur[] = {
{{ range .Config.VGA }} &{{ . }}cur,
{{ end }}
nil,
};
{{ range .Config.Code }}{{ . }}
{{ end }}
char* conffile = "{{ .Path }}";
`
// These are the two big code generation functions.
// data2c takes the file at path and creates a C byte array containing it.
func data2c(name string, path string) string {
var out []byte
var in []byte
if elf, err := elf.Open(path); err == nil {
elf.Close()
cwd, err := os.Getwd()
tmpf, err := ioutil.TempFile(cwd, name)
failOn(err)
run(nil, *shellhack, exec.Command(tools["strip"], "-o", tmpf.Name(), path))
in, err = ioutil.ReadAll(tmpf)
failOn(err)
tmpf.Close()
os.Remove(tmpf.Name())
} else {
var file *os.File
var err error
file, err = os.Open(path)
failOn(err)
in, err = ioutil.ReadAll(file)
failOn(err)
file.Close()
}
total := len(in)
out = []byte(fmt.Sprintf("static unsigned char ramfs_%s_code[] = {\n", name))
for len(in) > 0 {
for j := 0; j < 16 && len(in) > 0; j++ {
out = append(out, []byte(fmt.Sprintf("0x%02x, ", in[0]))...)
in = in[1:]
}
out = append(out, '\n')
}
out = append(out, []byte(fmt.Sprintf("0,\n};\nint ramfs_%s_len = %v;\n", name, total))...)
return string(out)
}
// confcode creates a kernel configuration header.
func confcode(path string, kern *kernel) []byte {
var rootcodes []string
var rootnames []string
for name, path := range kern.Ramfiles {
code := data2c(name, fromRoot(path))
rootcodes = append(rootcodes, code)
rootnames = append(rootnames, name)
}
vars := struct {
Path string
Config kernconfig
Rootnames []string
Rootcodes []string
}{
path,
kern.Config,
rootnames,
rootcodes,
}
tmpl := template.Must(template.New("kernconf").Parse(kernconfTmpl))
codebuf := &bytes.Buffer{}
failOn(tmpl.Execute(codebuf, vars))
return codebuf.Bytes()
}

View File

@ -1,184 +0,0 @@
/*
BUILDFILE FORMAT
A buildfile is a json object containing build objects. By convention, it's
broken out onto multiple lines indented by tabs, and the keys are TitleCased.
The environment varibles JEHANNE and ARCH are guarenteed to be set for the
buildfile. PATH is modified so that "$JEHANNE/hacking" is included.
Any key that takes a path or array of paths as its value has absolute paths
re-rooted to the jehanne tree and variables in the string expanded once.
The shell commands in the "Pre" and "Post" steps must be in the subset of
syntax that's accepted by both POSIX sh and rc. Practically, this means
arguments with a "=" must be single-quoted, "test" must be called as
"test" (not "["), "if" statements may not have an "else" clause, "switch"
statements may not be used, "for" statements may only have one body command,
and redirection to a file descriptor cannont be used.
BUILD OBJECT
A build object has the following keys and types:
Projects []string
Pre []string