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:
parent
6b473cd28f
commit
0c2dabf975
|
@ -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/
|
||||
|
|
|
@ -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
4
QA.sh
|
@ -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,2 +1,3 @@
|
|||
*
|
||||
!.gitignore
|
||||
!build
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
$JEHANNE/hacking/bin/rc $JEHANNE/arch/rc/cmd/template $@
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) &&
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"CrossCompiledPackages": {
|
||||
"Env": [
|
||||
"CROSS_PKGS_BUILD=1"
|
||||
],
|
||||
"Pre": [
|
||||
"./newlib/build.sh",
|
||||
"./mksh/build.sh",
|
||||
"./gcc/build.sh"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -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."
|
|
@ -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
|
|
@ -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": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
|
@ -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."
|
|
@ -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
|
10
devshell.sh
10
devshell.sh
|
@ -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-
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
Post []string
|
||||
Cflags []string
|
||||
Oflags []string
|
||||
Include []string
|
||||
SourceFiles []string
|
||||
ObjectFiles []string
|
||||
Libs []string
|
||||
Env []string
|
||||
SourceFilesCmd []string
|
||||
Program string
|
||||
Library string
|
||||
Install string
|
||||
Kernel kernel
|
||||
|
||||
These are the steps taken, in order:
|
||||
|
||||
Env
|
||||
Include
|
||||
Projects
|
||||
Pre
|
||||
Kernel
|
||||
[compile] SourceFiles SourceFilesCmd Cflags Program
|
||||
[link] ObjectFiles Libs Oflags Library
|
||||
Install
|
||||
Post
|
||||
|
||||
"[compile]" and "[link]" are steps synthesized from the specified keys.
|
||||
|
||||
The meaning of the keys is as follows:
|
||||
|
||||
"Env" is an array of environment variables to be put in the environment of
|
||||
every command run in a build. They are expanded once and only available for
|
||||
use in other steps.
|
||||
|
||||
"Include" is an array of buildfiles to "merge" into the current one. This
|
||||
is done before other projects are built.
|
||||
|
||||
"Projects" is an array of buildfiles to build in their entirety before
|
||||
starting the current build.
|
||||
|
||||
"Pre" is an array of commands to run before starting the build.
|
||||
|
||||
"Kernel" is a kernel build object. See the kernel object section.
|
||||
|
||||
"Cflags" is an array of flags to pass to the C compiler. They are in addition
|
||||
to the standard flags of
|
||||
|
||||
-std=c11 -c -I /$ARCH/include -I /sys/include -I .
|
||||
|
||||
The standard include paths are re-rooted to the jehanne tree if not on a jehanne
|
||||
system.
|
||||
|
||||
"SourceFilesCmd" is an array of C files where each one should result in an
|
||||
executable. If this key is provided, "SourceFiles" and "Program" are ignored.
|
||||
|
||||
"SourceFiles" is an array of C files that will ultimately produce a single
|
||||
binary or library, named by "Program" or "Library" respectively.
|
||||
|
||||
"Program" is the name of the final executable for the files specified by
|
||||
"SourceFiles".
|
||||
|
||||
"Oflags" is an array of flags to pass to the linker. They are in addition
|
||||
to the standard flags of
|
||||
|
||||
-o $program $objfiles -L /$ARCH/lib $libs
|
||||
|
||||
The lib path is re-rooted to the jehanne tree if not on a jehanne system.
|
||||
|
||||
"ObjectFiles" is an array of strings specifying object files to be linked
|
||||
into the final program specified by "Program". Any object files produced
|
||||
by the preceeding "[compile]" step are automatically added to this before
|
||||
beginning the "[link]" step.
|
||||
|
||||
"Libs" is a an array of library arguments to pass to the linker.
|
||||
|
||||
"Library" is the name of the archive resulting from bundling "SourceFiles"
|
||||
into a library. The resulting archive has 'ranlib' run on it automatically.
|
||||
|
||||
"Install" is a directory where the result of the "[link]" step is moved
|
||||
to. If it does not exist, it is created.
|
||||
|
||||
"Post" is an array of commands to run last during the build.
|
||||
|
||||
KERNEL OBJECT
|
||||
|
||||
A build object has the following keys and types:
|
||||
|
||||
Systab string
|
||||
Ramfiles map[string]string
|
||||
Config {
|
||||
Code []string
|
||||
Dev []string
|
||||
Ip []string
|
||||
Link []string
|
||||
Sd []string
|
||||
Uart []string
|
||||
VGA []string
|
||||
}
|
||||
|
||||
"Systab" is the header that defines the syscall table.
|
||||
|
||||
"Ramfiles" is an object of name, path pairs of binaries at "path" that will
|
||||
be baked into the kernel and available at a binary named "name".
|
||||
|
||||
"Config" is an object used to generate the kernel configuration source. "Dev",
|
||||
"Ip", "Sd", "Uart", "Link", and "VGA" control which drivers of the various
|
||||
types are included. "Code" is lines of arbitrary C code.
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var helptext = `
|
||||
The buildfile is looked for at these positions, in this order:
|
||||
|
||||
./$arg
|
||||
./$arg/build.json
|
||||
/sys/src/$arg.json
|
||||
/sys/src/$arg/build.json
|
||||
|
||||
If the buildfile argument is not provided, it defaults to "build.json".
|
||||
|
||||
After the buildfile, a number of regexps specifying targets may be provided.
|
||||
If a target matches any supplied regexp, it is acted on. These regexps only
|
||||
apply to the top-level buildfile.
|
||||
|
||||
BUILDFILE
|
||||
|
||||
See the build godoc for more information about the buildfile format.
|
||||
|
||||
ENVIRONMENT
|
||||
|
||||
ARCH is needed. Current acceptable vaules are: amd64
|
||||
|
||||
JEHANNE may be supplied to point at a jehanne tree.
|
||||
The default on Jehanne is "/".
|
||||
The default on Linux and OSX is to attempt to find the top level of a git
|
||||
repository.
|
||||
`
|
||||
|
||||
func init() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, " %s [options] [buildfile] [target...]\n\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
fmt.Fprintln(os.Stderr, helptext)
|
||||
fmt.Fprintln(os.Stderr, "Tools to be used with current settings:")
|
||||
fmt.Fprintf(os.Stderr, " prefix ($TOOLPREFIX): %q\n", os.Getenv("TOOLPREFIX"))
|
||||
for k, v := range tools {
|
||||
fmt.Fprintf(os.Stderr, " %s ($%s): %s\n", k, strings.ToUpper(k), v)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// +build !jehanne
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
jehanne = os.Getenv("JEHANNE")
|
||||
if jehanne != "" {
|
||||
return
|
||||
}
|
||||
// git is purely optional, for lazy people.
|
||||
out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
|
||||
if err == nil {
|
||||
jehanne = strings.TrimSpace(string(out))
|
||||
hackingAt := strings.LastIndex("/hacking", jehanne)
|
||||
if(hackingAt >= 0){
|
||||
jehanne = jehanne[0:hackingAt]
|
||||
}
|
||||
}
|
||||
if jehanne == "" {
|
||||
log.Fatal("Set the JEHANNE environment variable or run from a git checkout.")
|
||||
}
|
||||
|
||||
os.Setenv("PATH", strings.Join([]string{fromRoot("/hacking"), os.Getenv("PATH")}, string(os.PathListSeparator)))
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// +build jehanne
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
jehanne = os.Getenv("JEHANNE")
|
||||
if jehanne != "" {
|
||||
return
|
||||
}
|
||||
jehanne = "/"
|
||||
os.Setenv("path", strings.Join([]string{fromRoot("/util"), os.Getenv("path")}, string(os.PathListSeparator)))
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var config = struct {
|
||||
Jehanne string
|
||||
Args []string
|
||||
Except map[string]bool
|
||||
CmdName string
|
||||
FullPath string
|
||||
Src string
|
||||
Uroot string
|
||||
Cwd string
|
||||
Bbsh string
|
||||
|
||||
Goroot string
|
||||
Gosrcroot string
|
||||
Arch string
|
||||
Goos string
|
||||
Gopath string
|
||||
TempDir string
|
||||
Go string
|
||||
Debug bool
|
||||
Fail bool
|
||||
}{
|
||||
Jehanne: os.Getenv("JEHANNE"),
|
||||
Except: map[string]bool{"sysconf.json": true},
|
||||
}
|
||||
|
||||
type fixup func(string, map[string]interface{})
|
||||
|
||||
func cflags(n string, jsmap map[string]interface{}) {
|
||||
if _, ok := jsmap["Cflags"]; ok {
|
||||
log.Printf("Deleting Cflags from %v", n)
|
||||
delete(jsmap, "Cflags")
|
||||
// TODO: once we have another ARCH, use it.
|
||||
a := []string{"/arch/amd64/include/cflags.json"}
|
||||
switch tval := jsmap["Include"].(type) {
|
||||
case []interface{}:
|
||||
for _, v := range tval {
|
||||
a = append(a, v.(string))
|
||||
}
|
||||
}
|
||||
jsmap["Include"] = a
|
||||
}
|
||||
}
|
||||
|
||||
func removeempty(n string, jsmap map[string]interface{}) {
|
||||
for key, val := range jsmap {
|
||||
switch tval := val.(type) {
|
||||
case map[string]interface{}:
|
||||
log.Printf("%s: tval %s", n, tval)
|
||||
if len(tval) == 0 {
|
||||
delete(jsmap, key)
|
||||
}
|
||||
case []interface{}:
|
||||
if len(tval) == 0 {
|
||||
delete(jsmap, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkname(n string, jsmap map[string]interface{}) {
|
||||
if _, ok := jsmap["Name"]; !ok {
|
||||
log.Print("File %v has no \"Name\" key", n)
|
||||
}
|
||||
}
|
||||
|
||||
func one(n string, f ...fixup) error {
|
||||
buf, err := ioutil.ReadFile(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var jsmap map[string]interface{}
|
||||
if err := json.Unmarshal(buf, &jsmap); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Debug {
|
||||
log.Printf("%v: %v", n, jsmap)
|
||||
}
|
||||
|
||||
mapname := jsmap["Name"].(string)
|
||||
delete(jsmap, "Name")
|
||||
var nmap = make(map[string]map[string]interface{})
|
||||
nmap[mapname] = jsmap
|
||||
buf, err = json.MarshalIndent(nmap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = append(buf, '\n')
|
||||
|
||||
if config.Debug {
|
||||
os.Stdout.Write(buf)
|
||||
} else {
|
||||
ioutil.WriteFile(n, buf, 0666)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if config.Jehanne == "" {
|
||||
log.Fatalf("Please set $JEHANNE")
|
||||
}
|
||||
|
||||
if len(config.Args) == 0 {
|
||||
config.Args = []string{config.Jehanne}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
flag.BoolVar(&config.Debug, "d", true, "Enable debug prints")
|
||||
flag.Parse()
|
||||
config.Args = flag.Args()
|
||||
for _, n := range config.Args {
|
||||
err = filepath.Walk(n, func(name string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
n := fi.Name()
|
||||
if len(n) < 5 || n[len(n)-5:] != ".json" {
|
||||
return nil
|
||||
}
|
||||
if config.Except[fi.Name()] {
|
||||
return nil
|
||||
}
|
||||
todo := []fixup{removeempty, checkname}
|
||||
log.Printf("process %s", name)
|
||||
err = one(name, todo...)
|
||||
if err != nil {
|
||||
log.Printf("%s: %s\n", name, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("%v", err)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
a := flag.Args()
|
||||
if len(a) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "[%v]usage: data2s name input-file (writes to stdout)\n", a)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
n := a[0]
|
||||
i := a[1]
|
||||
in, err := ioutil.ReadFile(i)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
total := len(in)
|
||||
|
||||
fmt.Printf("unsigned char %vcode[] = {\n", n)
|
||||
for len(in) > 0 {
|
||||
for j := 0; j < 16 && len(in) > 0; j++ {
|
||||
fmt.Printf("0x%02x, ", in[0])
|
||||
in = in[1:]
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
}
|
||||
|
||||
fmt.Printf("0,\n};\nint %vlen = %v;\n", n, total)
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"debug/elf"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var dry = flag.Bool("dryrun", true, "don't really do it")
|
||||
|
||||
func gencode(w io.Writer, n, t string, m []byte, start, end uint64) {
|
||||
fmt.Fprintf(os.Stderr, "Write %v %v start %v end %v\n", n, t, start, end)
|
||||
fmt.Fprintf(w, "int %v_%v_start = %v;\n", n, t, start)
|
||||
fmt.Fprintf(w, "int %v_%v_end = %v;\n", n, t, end)
|
||||
fmt.Fprintf(w, "int %v_%v_len = %v;\n", n, t, end-start)
|
||||
fmt.Fprintf(w, "uint8_t %v_%v_out[] = {\n", n, t)
|
||||
for i := uint64(start); i < end; i += 16 {
|
||||
for j := uint64(0); i+j < end && j < 16; j++ {
|
||||
fmt.Fprintf(w, "0x%02x, ", m[j+i])
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
}
|
||||
func main() {
|
||||
flag.Parse()
|
||||
a := flag.Args()
|
||||
for _, n := range a {
|
||||
f, err := elf.Open(n)
|
||||
if err != nil {
|
||||
fmt.Printf("%v %v\n", n, err)
|
||||
continue
|
||||
}
|
||||
var dataend, codeend, end uint64
|
||||
var datastart, codestart, start uint64
|
||||
datastart, codestart, start = math.MaxUint64, math.MaxUint64, math.MaxUint64
|
||||
mem := []byte{}
|
||||
for _, v := range f.Progs {
|
||||
if v.Type != elf.PT_LOAD {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "processing %v\n", v)
|
||||
// MUST alignt to 2M page boundary.
|
||||
// then MUST allocate a []byte that
|
||||
// is the right size. And MUST
|
||||
// see if by some off chance it
|
||||
// joins to a pre-existing segment.
|
||||
// It's easier than it seems. We produce ONE text
|
||||
// array and ONE data array. So it's a matter of creating
|
||||
// a virtual memory space with an assumed starting point of
|
||||
// 0x200000, and filling it. We just grow that as needed.
|
||||
|
||||
curstart := v.Vaddr & ^uint64(0xfff) // 0x1fffff)
|
||||
curend := v.Vaddr + v.Memsz
|
||||
fmt.Fprintf(os.Stderr, "s %x e %x\n", curstart, curend)
|
||||
if curend > end {
|
||||
nmem := make([]byte, curend)
|
||||
copy(nmem, mem)
|
||||
mem = nmem
|
||||
}
|
||||
if curstart < start {
|
||||
start = curstart
|
||||
}
|
||||
|
||||
if v.Flags&elf.PF_X == elf.PF_X {
|
||||
if curstart < codestart {
|
||||
codestart = curstart
|
||||
}
|
||||
if curend > codeend {
|
||||
codeend = curend
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "code s %v e %v\n", codestart, codeend)
|
||||
} else {
|
||||
if curstart < datastart {
|
||||
datastart = curstart
|
||||
}
|
||||
if curend > dataend {
|
||||
dataend = curend
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "data s %v e %v\n", datastart, dataend)
|
||||
}
|
||||
for i := uint64(0); i < v.Filesz; i++ {
|
||||
if amt, err := v.ReadAt(mem[v.Vaddr+i:], int64(i)); err != nil && err != io.EOF {
|
||||
fmt.Fprintf(os.Stderr, "%v: %v\n", amt, err)
|
||||
os.Exit(1)
|
||||
} else if amt == 0 {
|
||||
if i < v.Filesz {
|
||||
fmt.Fprintf(os.Stderr, "%v: Short read: %v of %v\n", v, i, v.Filesz)
|
||||
os.Exit(1)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
i = i + uint64(amt)
|
||||
fmt.Fprintf(os.Stderr, "i now %v\n", i)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Processed %v\n", v)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "gencode\n")
|
||||
// Gen code to stdout. For each file, create an array, a start, and an end variable.
|
||||
w := bufio.NewWriter(os.Stdout)
|
||||
_, file := path.Split(n)
|
||||
gencode(w, file, "code", mem, codestart, codeend)
|
||||
gencode(w, file, "data", mem, datastart, dataend)
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
dirPermissions = 0755
|
||||
)
|
||||
|
||||
type Fetch struct {
|
||||
Upstream string
|
||||
Digest map[string]string
|
||||
Compress string
|
||||
RemovePrefix bool
|
||||
Exclude []string
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
j, err := ioutil.ReadFile("fetch.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fetches := make(map[string]Fetch)
|
||||
if err := json.Unmarshal(j, &fetches); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for name, f := range(fetches) {
|
||||
if _, err := os.Stat(name); err == nil {
|
||||
log.Printf("Fetch: skip %v (already present)", name)
|
||||
} else {
|
||||
log.Printf("Fetch: %v from %v", name, f.Upstream)
|
||||
if err := do(&f, name); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func do(f *Fetch, name string) error {
|
||||
fname := fetch(f)
|
||||
s, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(fname)
|
||||
|
||||
os.MkdirAll(name, dirPermissions)
|
||||
|
||||
var unZ io.Reader
|
||||
switch f.Compress {
|
||||
case "gzip":
|
||||
unZ, err = gzip.NewReader(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "bzip2":
|
||||
unZ = bzip2.NewReader(s)
|
||||
default:
|
||||
unZ = s
|
||||
}
|
||||
|
||||
ar := tar.NewReader(unZ)
|
||||
h, err := ar.Next()
|
||||
untar:
|
||||
for ; err == nil; h, err = ar.Next() {
|
||||
n := h.Name
|
||||
if f.RemovePrefix {
|
||||
n = strings.SplitN(n, "/", 2)[1]
|
||||
}
|
||||
for _, ex := range f.Exclude {
|
||||
if strings.HasPrefix(n, ex) {
|
||||
continue untar
|
||||
}
|
||||
}
|
||||
n = path.Join(name, n)
|
||||
if h.FileInfo().IsDir() {
|
||||
os.MkdirAll(n, dirPermissions)
|
||||
continue
|
||||
}
|
||||
os.MkdirAll(path.Dir(n), dirPermissions)
|
||||
out, err := os.Create(n)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if n, err := io.Copy(out, ar); n != h.Size || err != nil {
|
||||
return err
|
||||
}
|
||||
out.Close()
|
||||
if err := os.Chmod(n, h.FileInfo().Mode()); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type match struct {
|
||||
hash.Hash
|
||||
Good []byte
|
||||
Name string
|
||||
}
|
||||
|
||||
func (m match) OK() bool {
|
||||
return bytes.Equal(m.Good, m.Hash.Sum(nil))
|
||||
}
|
||||
|
||||
func fetch(v *Fetch) string {
|
||||
if len(v.Digest) == 0 {
|
||||
log.Fatal("no checksums specifed")
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "cmdVendor")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
req, err := http.NewRequest("GET", v.Upstream, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DisableCompression: true,
|
||||
},
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var digests []match
|
||||
for k, v := range v.Digest {
|
||||
g, err := hex.DecodeString(v)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
switch k {
|
||||
case "sha224":
|
||||
digests = append(digests, match{sha256.New224(), g, k})
|
||||
case "sha256":
|
||||
digests = append(digests, match{sha256.New(), g, k})
|
||||
case "sha384":
|
||||
digests = append(digests, match{sha512.New384(), g, k})
|
||||
case "sha512":
|
||||
digests = append(digests, match{sha512.New(), g, k})
|
||||
}
|
||||
}
|
||||
ws := make([]io.Writer, len(digests))
|
||||
for i := range digests {
|
||||
ws[i] = digests[i]
|
||||
}
|
||||
w := io.MultiWriter(ws...)
|
||||
|
||||
if _, err := io.Copy(f, io.TeeReader(res.Body, w)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, h := range digests {
|
||||
if !h.OK() {
|
||||
log.Fatalf("mismatched %q hash\n\tWanted %x\n\tGot %x\n", h.Name, h.Good, h.Hash.Sum(nil))
|
||||
}
|
||||
}
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
func run(exe string, arg ...string) error {
|
||||
cmd := exec.Command(exe, arg...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Printf("usage: jsonpretty input.json [output.json]\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var jsmap map[string]interface{}
|
||||
if err := json.Unmarshal(buf, &jsmap); err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for key, val := range jsmap {
|
||||
switch tval := val.(type) {
|
||||
case map[string]interface{}:
|
||||
if len(tval) == 0 {
|
||||
delete(jsmap, key)
|
||||
}
|
||||
case []interface{}:
|
||||
if len(tval) == 0 {
|
||||
delete(jsmap, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf, err = json.MarshalIndent(jsmap, "", "\t")
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
buf = append(buf, '\n')
|
||||
|
||||
if len(os.Args) == 3 {
|
||||
ioutil.WriteFile(os.Args[2], buf, 0666)
|
||||
} else {
|
||||
os.Stdout.Write(buf)
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
)
|
||||
|
||||
const LRES = 3
|
||||
|
||||
var kernel = flag.String("k", "9k", "kernel name")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
n := flag.Args()[0]
|
||||
d, err := os.Open(n)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
f, err := elf.Open(*kernel)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var codeend uint64
|
||||
var codestart uint64 = math.MaxUint64
|
||||
|
||||
for _, v := range f.Progs {
|
||||
if v.Type != elf.PT_LOAD {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "processing %v\n", v)
|
||||
// MUST alignt to 2M page boundary.
|
||||
// then MUST allocate a []byte that
|
||||
// is the right size. And MUST
|
||||
// see if by some off chance it
|
||||
// joins to a pre-existing segment.
|
||||
// It's easier than it seems. We produce ONE text
|
||||
// array and ONE data array. So it's a matter of creating
|
||||
// a virtual memory space with an assumed starting point of
|
||||
// 0x200000, and filling it. We just grow that as needed.
|
||||
|
||||
curstart := v.Vaddr
|
||||
curend := v.Vaddr + v.Memsz
|
||||
// magic numbers, BAH!
|
||||
if curstart < uint64(0xffffffff00000000) {
|
||||
curstart += 0xfffffffff0000000
|
||||
curend += 0xfffffffff0000000
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "s %x e %x\n", curstart, curend)
|
||||
if v.Flags&elf.PF_X == elf.PF_X {
|
||||
if curstart < codestart {
|
||||
codestart = curstart
|
||||
}
|
||||
if curend > codeend {
|
||||
codeend = curend
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "code s %x e %x\n", codestart, codeend)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "code s %x e %x\n", codestart, codeend)
|
||||
s, err := f.Symbols()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// maybe we should stop doing LRES ...
|
||||
symname := make([]string, codeend-codestart)
|
||||
for i := range symname {
|
||||
symname[i] = fmt.Sprintf("[0x%x]", codestart+uint64(i))
|
||||
}
|
||||
for _, v := range s {
|
||||
vstart := v.Value
|
||||
vend := v.Value + v.Size
|
||||
if v.Value > codeend {
|
||||
continue
|
||||
}
|
||||
if v.Value+v.Size < codestart {
|
||||
continue
|
||||
}
|
||||
if vstart < codestart {
|
||||
v.Value = codestart
|
||||
}
|
||||
if vend > codeend {
|
||||
vend = codeend
|
||||
}
|
||||
for i := vstart; i < vend; i++ {
|
||||
symname[i-codestart] = v.Name
|
||||
}
|
||||
}
|
||||
symname[0] = "Total ms"
|
||||
symname[1<<LRES] = "Unknown"
|
||||
// now dump the info ...
|
||||
count := uint32(0)
|
||||
pc := codestart
|
||||
for {
|
||||
if err := binary.Read(d, binary.BigEndian, &count); err != nil {
|
||||
if err != io.EOF {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if count > 0 {
|
||||
fmt.Printf("%s %d\n", symname[pc-codestart], count)
|
||||
}
|
||||
pc += (1 << LRES)
|
||||
}
|
||||
}
|
|
@ -1,385 +0,0 @@
|
|||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type SyscallConf struct {
|
||||
Ret []string
|
||||
Args []string
|
||||
Name string
|
||||
Id uint32
|
||||
}
|
||||
|
||||
type Sysconf struct {
|
||||
Syscalls []SyscallConf
|
||||
}
|
||||
|
||||
func usage(msg string) {
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
fmt.Fprint(os.Stderr, "Usage: ksyscalls path/to/sysconf.json\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type SyscallWrapper struct {
|
||||
Id uint32
|
||||
Name string
|
||||
Vars []string
|
||||
CommonCode string
|
||||
ExecCode string
|
||||
EntryPrint string
|
||||
ExitPrint string
|
||||
SysRetField string
|
||||
DefaultRet string
|
||||
}
|
||||
|
||||
type KernelCode struct {
|
||||
Externs []string
|
||||
Wrappers []SyscallWrapper
|
||||
}
|
||||
|
||||
func uregArg(index int) string{
|
||||
switch(index){
|
||||
case 0:
|
||||
return "di";
|
||||
case 1:
|
||||
return "si";
|
||||
case 2:
|
||||
return "dx";
|
||||
case 3:
|
||||
return "r10";
|
||||
case 4:
|
||||
return "r8";
|
||||
case 5:
|
||||
return "r9";
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func sysret(t string) string {
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return "i"
|
||||
case "long", "int64_t":
|
||||
return "vl"
|
||||
case "uintptr_t", "void":
|
||||
return "p"
|
||||
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*":
|
||||
return "v"
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
}
|
||||
|
||||
func formatArg(i int, t string) string{
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%d\", a%d);\n", i)
|
||||
case "unsigned int", "uint32_t":
|
||||
/* unsigned int is reserved for flags */
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#ux\", a%d);\n", i)
|
||||
case "long", "int64_t":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%lld\", a%d);\n", i)
|
||||
case "unsigned long", "uint64_t":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#lud\", a%d);\n", i)
|
||||
case "void*", "uint8_t*", "const void*", "const uint8_t*":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p\", a%d);\n", i)
|
||||
case "int32_t*", "int*", "const int32_t*", "const int*":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p(%%d)\", a%d, a%d);\n", i, i)
|
||||
case "const char*", "char*":
|
||||
return fmt.Sprintf("\tfmtuserstring(fmt, a%d);\n", i)
|
||||
case "const char**", "char**":
|
||||
return fmt.Sprintf("\tfmtuserstringlist(fmt, a%d);\n", i);
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
}
|
||||
|
||||
func formatRet(t string) string{
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%d\", ret->%s);\n", sysret(t))
|
||||
case "unsigned int", "uint32_t":
|
||||
/* unsigned int is reserved for flags */
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#ux\", ret->%s);\n", sysret(t))
|
||||
case "long", "int64_t":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%lld\", ret->%s);\n", sysret(t))
|
||||
case "unsigned long", "uint64_t", "void":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#llud\", ret->%s);\n", sysret(t))
|
||||
case "void*", "uintptr_t", "const void*", "const uintptr_t":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p\", ret->%s);\n", sysret(t))
|
||||
case "int32_t*", "int*", "const int32_t*", "const int*":
|
||||
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p(%%d)\", ret->%s, *ret->%s);\n", sysret(t), sysret(t))
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
}
|
||||
|
||||
func generateKernelCode(calls []SyscallConf){
|
||||
code := new(KernelCode)
|
||||
|
||||
for _, call := range(calls) {
|
||||
/* extern definitions */
|
||||
ext := "extern " + call.Ret[0] + " sys" + call.Name + "("
|
||||
if len(call.Args) == 0 {
|
||||
ext += "void";
|
||||
} else {
|
||||
for i, a := range(call.Args){
|
||||
if i > 0 {
|
||||
ext += ", "
|
||||
}
|
||||
ext += fmt.Sprintf("%s", a)
|
||||
}
|
||||
}
|
||||
ext += ");\n"
|
||||
|
||||
wcall := new(SyscallWrapper)
|
||||
wcall.Id = call.Id
|
||||
wcall.Name = call.Name
|
||||
wcall.SysRetField = sysret(call.Ret[0])
|
||||
wcall.DefaultRet = fmt.Sprintf("ret.%s = (%s)-1;", wcall.SysRetField, call.Ret[0])
|
||||
|
||||
for i, a := range(call.Args){
|
||||
wcall.Vars = append(wcall.Vars, fmt.Sprintf("%s a%v;", a, i))
|
||||
wcall.CommonCode += fmt.Sprintf("\ta%v = (%s)ureg->%s;\n", i, a, uregArg(i))
|
||||
}
|
||||
wcall.ExecCode += "\tret->" + wcall.SysRetField + " = sys" + call.Name + "("
|
||||
for i, _ := range(call.Args){
|
||||
if i > 0 {
|
||||
wcall.ExecCode += ", "
|
||||
}
|
||||
wcall.ExecCode += fmt.Sprintf("a%v", i)
|
||||
}
|
||||
wcall.ExecCode += ");"
|
||||
|
||||
if call.Name == "pwrite"{
|
||||
wcall.EntryPrint += formatArg(0, call.Args[0])
|
||||
wcall.EntryPrint += "\tfmtrwdata(fmt, (char*)a1, MIN(a2, 64));\n"
|
||||
wcall.EntryPrint += formatArg(2, call.Args[2])
|
||||
wcall.EntryPrint += formatArg(3, call.Args[3])
|
||||
} else {
|
||||
for i, a := range(call.Args){
|
||||
wcall.EntryPrint += formatArg(i, a)
|
||||
}
|
||||
}
|
||||
|
||||
wcall.ExitPrint += formatRet(call.Ret[0])
|
||||
if call.Name == "pread"{
|
||||
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(1), uregArg(2))
|
||||
} else if call.Name == "fd2path"{
|
||||
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(1), uregArg(2))
|
||||
} else if call.Name == "await"{
|
||||
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(0), uregArg(1))
|
||||
} else if call.Name == "errstr"{
|
||||
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(0), uregArg(1))
|
||||
}
|
||||
|
||||
code.Wrappers = append(code.Wrappers, *wcall)
|
||||
code.Externs = append(code.Externs, ext)
|
||||
}
|
||||
|
||||
tmpl, err := template.New("systab.c").Parse(`/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* automatically generated by ksyscalls */
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "../port/error.h"
|
||||
#include "ureg.h"
|
||||
|
||||
extern void fmtrwdata(Fmt* f, char* a, int n);
|
||||
extern void fmtuserstring(Fmt* f, const char* a);
|
||||
extern void fmtuserstringlist(Fmt* f, const char** argv);
|
||||
|
||||
{{ range .Externs }}{{.}}{{ end }}
|
||||
{{ range .Wrappers }}
|
||||
static void
|
||||
wrap_{{ .Name }}(ScRet* ret, Ureg* ureg)
|
||||
{
|
||||
{{ range .Vars }}{{.}}
|
||||
{{ end }}
|
||||
{{ .CommonCode }}
|
||||
{{ .ExecCode }}
|
||||
}
|
||||
{{ end }}
|
||||
int nsyscall = {{len .Wrappers}};
|
||||
|
||||
ScRet
|
||||
default_syscall_ret(int syscall)
|
||||
{
|
||||
static ScRet zero;
|
||||
ScRet ret = zero;
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
{{ .DefaultRet }}
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
ret.vl = -1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char*
|
||||
syscall_name(int syscall)
|
||||
{
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
return "{{ .Name }}";
|
||||
{{ end }}
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dispatch_syscall(int syscall, Ureg* ureg, ScRet* ret)
|
||||
{
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
wrap_{{ .Name }}(ret, ureg);
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
panic("dispatch_syscall: bad sys call number %d pc %#p\n", syscall, ureg->ip);
|
||||
}
|
||||
}
|
||||
|
||||
{{ range .Wrappers }}
|
||||
static void
|
||||
enter_{{ .Name }}(Fmt* fmt, Ureg* ureg)
|
||||
{
|
||||
{{ range .Vars }}{{.}}
|
||||
{{ end }}
|
||||
{{ .CommonCode }}
|
||||
jehanne_fmtprint(fmt, "{{ .Name }} %#p >", ureg->ip);
|
||||
if(up->notified)
|
||||
jehanne_fmtprint(fmt, "!");
|
||||
{{ .EntryPrint }}
|
||||
jehanne_fmtprint(fmt, "\n");
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
char*
|
||||
syscallfmt(int syscall, Ureg* ureg)
|
||||
{
|
||||
Fmt fmt;
|
||||
jehanne_fmtstrinit(&fmt);
|
||||
jehanne_fmtprint(&fmt, "%d %s ", up->pid, up->text);
|
||||
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
enter_{{ .Name }}(&fmt, ureg);
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
panic("syscallfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
|
||||
}
|
||||
|
||||
return jehanne_fmtstrflush(&fmt);
|
||||
}
|
||||
|
||||
{{ range .Wrappers }}
|
||||
static void
|
||||
exit_{{ .Name }}(Fmt* fmt, Ureg* ureg, ScRet* ret)
|
||||
{
|
||||
jehanne_fmtprint(fmt, "{{ .Name }} %#p <", ureg->ip);
|
||||
if(up->notified)
|
||||
jehanne_fmtprint(fmt, "!");
|
||||
{{ .ExitPrint }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
char*
|
||||
sysretfmt(int syscall, Ureg* ureg, ScRet* ret, uint64_t start, uint64_t stop)
|
||||
{
|
||||
Fmt fmt;
|
||||
jehanne_fmtstrinit(&fmt);
|
||||
jehanne_fmtprint(&fmt, "%d %s ", up->pid, up->text);
|
||||
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
exit_{{ .Name }}(&fmt, ureg, ret);
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
panic("sysretfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
|
||||
}
|
||||
|
||||
if(0 > ret->vl){
|
||||
jehanne_fmtprint(&fmt, " %s %#llud %#llud\n", up->syserrstr, start, stop-start);
|
||||
} else {
|
||||
jehanne_fmtprint(&fmt, " \"\" %#llud %#llud\n", start, stop-start);
|
||||
}
|
||||
|
||||
return jehanne_fmtstrflush(&fmt);
|
||||
}
|
||||
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
usage("no path to sysconf.json")
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(flag.Arg(0))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var sysconf Sysconf
|
||||
err = json.Unmarshal(buf, &sysconf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
generateKernelCode(sysconf.Syscalls)
|
||||
|
||||
}
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
* This file is part of the UCB release of Plan 9. It is subject to the license
|
||||
* terms in the LICENSE file found in the top-level directory of this
|
||||
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
||||
* part of the UCB release of Plan 9, including this file, may be copied,
|
||||
* modified, propagated, or distributed except according to the terms contained
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type Syscall struct {
|
||||
Ret []string
|
||||
Args []string
|
||||
Name string
|
||||
Id uint32
|
||||
Define string
|
||||
Sysname string
|
||||
Libname string
|
||||
Fudge string `json:"-"`
|
||||
GoArgs []string `json:"-"`
|
||||
Ret0 string `json:"-"`
|
||||
}
|
||||
|
||||
type Syserror struct {
|
||||
Name string
|
||||
String string
|
||||
Id uint32
|
||||
}
|
||||
|
||||
type Bootmethods struct {
|
||||
Name string
|
||||
Config string
|
||||
Connect string
|
||||
Arg string
|
||||
}
|
||||
|
||||
type Sysconf struct {
|
||||
Syscalls []Syscall
|
||||
Syserrors []Syserror
|
||||
Bootmethods []Bootmethods
|
||||
}
|
||||
|
||||
var mode = flag.String("mode", "", "must be one of: sys.h, sysdecl.h, syscallfiles, systab.c, error.h, errstr.h, sys_jehanne.s, sysnum.go")
|
||||
var outpath = flag.String("o", "", "path/to/output.c")
|
||||
|
||||
func usage(msg string) {
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
fmt.Fprint(os.Stderr, "Usage: mksys [-o outpath] -mode=MODE path/to/sysconf.json\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
usage("no path to sysconf.json")
|
||||
}
|
||||
|
||||
outfile := os.Stdout
|
||||
if *mode != "syscallfiles" && *outpath != "" {
|
||||
of, err := os.Create(*outpath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
outfile = of
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(flag.Arg(0))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var sysconf Sysconf
|
||||
err = json.Unmarshal(buf, &sysconf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
syscalls := sysconf.Syscalls
|
||||
syserrors := sysconf.Syserrors
|
||||
bootmethods := sysconf.Bootmethods
|
||||
for i := range syscalls {
|
||||
if syscalls[i].Define == "" {
|
||||
syscalls[i].Define = strings.ToUpper(syscalls[i].Name)
|
||||
}
|
||||
if syscalls[i].Sysname == "" {
|
||||
syscalls[i].Sysname = "sys" + syscalls[i].Name
|
||||
}
|
||||
if syscalls[i].Libname == "" {
|
||||
syscalls[i].Libname = syscalls[i].Name
|
||||
}
|
||||
}
|
||||
|
||||
switch *mode {
|
||||
case "sys_jehanne.s":
|
||||
if os.Getenv("ARCH") != "amd64" {
|
||||
usage("ARCH unsupported or not set")
|
||||
}
|
||||
syscallargs := []string{"DI", "SI", "DX", "R10", "R8", "R9"}
|
||||
//funcallregs := []string{ "DI", "SI", "DX", "CX", "R8", "R9" };
|
||||
for i := range syscalls {
|
||||
goargs := []string{}
|
||||
fpoff := 0
|
||||
for k := range syscalls[i].Args {
|
||||
switch syscalls[i].Args[k] {
|
||||
case "int32_t", "uint32_t":
|
||||
goargs = append(goargs, fmt.Sprintf("MOVL arg%d+%d(FP), %s", k, fpoff, syscallargs[k]))
|
||||
fpoff += 4
|
||||
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*", "int64_t":
|
||||
fpoff = (fpoff + 7) & ^7
|
||||
goargs = append(goargs, fmt.Sprintf("MOVQ arg%d+%d(FP), %s", k, fpoff, syscallargs[k]))
|
||||
fpoff += 8
|
||||
default:
|
||||
log.Fatalf("unsupported arg %s in syscall: %v", syscalls[i].Args[k], syscalls[i])
|
||||
}
|
||||
}
|
||||
syscalls[i].GoArgs = goargs
|
||||
switch syscalls[i].Ret[0] {
|
||||
case "int32_t", "uint32_t":
|
||||
syscalls[i].Ret0 = fmt.Sprintf("MOVL AX, ret+%d(FP)", fpoff)
|
||||
fpoff += 4
|
||||
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*", "int64_t":
|
||||
fpoff = (fpoff + 7) & ^7
|
||||
syscalls[i].Ret0 = fmt.Sprintf("MOVQ AX, ret+%d(FP)", fpoff)
|
||||
fpoff += 8
|
||||
default:
|
||||
log.Fatalf("unsupported Ret[0] in syscall: %v", syscalls[i])
|
||||
}
|
||||
}
|
||||
tmpl, err := template.New("sys_jehanne.s").Parse(`/* automatically generated by mksys */
|
||||
/* System calls for AMD64, Jehanne */
|
||||
#include "go_asm.h"
|
||||
#include "go_tls.h"
|
||||
#include "textflag.h"
|
||||
{{ range . }}
|
||||
TEXT runtime·{{ .Libname }}(SB),NOSPLIT,$0
|
||||
{{ range .GoArgs }} {{ . }}
|
||||
{{ end }} MOVQ ${{ .Id }}, AX
|
||||
SYSCALL
|
||||
{{ .Ret0 }}
|
||||
RET
|
||||
{{ end }}
|
||||
`)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(outfile, syscalls)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
case "syscallfiles":
|
||||
if os.Getenv("ARCH") != "amd64" {
|
||||
usage("ARCH unsupported or not set")
|
||||
}
|
||||
tmpl, err := template.New("syscall.s").Parse(`/* automatically generated by mksys */
|
||||
.globl {{ .Libname }}
|
||||
{{ .Libname }}:
|
||||
movq %rcx, %r10 /* rcx gets smashed by systenter. Use r10.*/
|
||||
movq ${{ .Id }},%rax /* Put the system call into rax, just like linux. */
|
||||
syscall
|
||||
ret
|
||||
`)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for i := range syscalls {
|
||||
|
||||
path := path.Join(*outpath, syscalls[i].Libname+".s")
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(file, syscalls[i])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
case "sysnum.go":
|
||||
tmpl, err := template.New("sysnum.go").Parse(`// automatically generated by mksys
|
||||
package syscall
|
||||
|
||||
const(
|
||||
{{ range . }} SYS_{{ .Define }} = {{ .Id }}
|
||||
{{ end }}
|
||||
)
|
||||
`)
|
||||
err = tmpl.Execute(outfile, syscalls)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
case "sys.h":
|
||||
tmpl, err := template.New("sys.h").Parse(`/* automatically generated by mksys */
|
||||
{{ range . }}#define {{ .Define }} {{ .Id }}
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(outfile, syscalls)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
case "sysdecl.h":
|
||||
tmpl, err := template.New("sysdecl.h").Parse(`/* automatically generated by mksys */
|
||||
{{ range . }}extern {{ .Ret0 }} {{ .Libname }}({{ range $i, $e := .Args }}{{ if $i }}, {{ end }}{{ $e }}{{ end }});
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(outfile, syscalls)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
case "systab.c":
|
||||
for i := range syscalls {
|
||||
var fudge string
|
||||
switch syscalls[i].Ret[0] {
|
||||
case "int32_t":
|
||||
fudge = "{ .i = -1 }"
|
||||
case "int64_t":
|
||||
fudge = "{ .vl = -1ll }"
|
||||
case "void*", "char*":
|
||||
fudge = "{ .v = (void*)-1ll }"
|
||||
default:
|
||||
log.Fatalf("unsupported Ret[0] in syscall: %v", syscalls[i])
|
||||
}
|
||||
if syscalls[i].Fudge == "" {
|
||||
syscalls[i].Fudge = fudge
|
||||
}
|
||||
|
||||
syscalls[i].Ret0 = syscalls[i].Ret[0]
|
||||
}
|
||||
tmpl, err := template.New("systab.c").Parse(`/* automatically generated by mksys */
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include <9syscall/sys.h>
|
||||
|
||||
{{ range . }}extern void {{ .Sysname }}(ScRet*, ScArg, ScArg, ScArg, ScArg, ScArg, ScArg);
|
||||
{{ end }}
|
||||
Systab systab[] = {
|
||||
{{ range . }}[{{ .Define }}] { "{{ .Name }}", {{ .Sysname }}, {{ .Fudge }} },
|
||||
{{ end }}
|
||||
};
|
||||
int nsyscall = nelem(systab);
|
||||
`)
|
||||
err = tmpl.Execute(outfile, syscalls)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
case "error.h":
|
||||
tmpl, err := template.New("error.h").Parse(`/* automatically generated by mksys */
|
||||
{{ range . }}extern char {{ .Name }}[]; /* {{ .String }} */
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(outfile, syserrors)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
case "errstr.h":
|
||||
tmpl, err := template.New("errstr.h").Parse(`/* automatically generated by mksys */
|
||||
{{ range . }}char {{ .Name }}[] = "{{ .String }}";
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(outfile, syserrors)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "bootamd64cpu.c":
|
||||
tmpl, err := template.New("bootamd64cpu.c").Parse(`/* automatically generated by mksys */
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
#include "../boot/boot.h"
|
||||
|
||||
Method method[] = {
|
||||
{{ range . }}{ "{{.Name}}", {{.Config}}, {{.Connect}}, "{{.Arg}}", },
|
||||
{{ end }}
|
||||
{ nil },
|
||||
};
|
||||
|
||||
int cpuflag = 1;
|
||||
char* rootdir = "/root";
|
||||
char* bootdisk = "#S/sdE0/";
|
||||
extern void boot(int, char**);
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
boot(argc, argv);
|
||||
}
|
||||
int (*cfs)(int) = 0;
|
||||
`)
|
||||
err = tmpl.Execute(outfile, bootmethods)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
/*
|
||||
first word
|
||||
high 8 bits is ee, which is an invalid address on amd64.
|
||||
next 8 bits is protocol version
|
||||
next 16 bits is unused, MBZ. Later, we can make it a packet type.
|
||||
next 16 bits is core id
|
||||
next 8 bits is unused
|
||||
next 8 bits is # words following.
|
||||
|
||||
second word is time in ns. (soon to be tsc ticks)
|
||||
|
||||
Third and following words are PCs, there must be at least one of them.
|
||||
*/
|
||||
type sample struct {
|
||||
Wordcount, _ uint8
|
||||
Coreid uint16
|
||||
_ uint16
|
||||
Version, Marker uint8
|
||||
Ns uint64
|
||||
}
|
||||
|
||||
type backtrace struct {
|
||||
Pcs []uint64
|
||||
}
|
||||
|
||||
/* the docs lie. Perl expects Count to be zero. I only wasted a day figuring this out. */
|
||||
type hdr struct {
|
||||
Count, Slots, Format, Period, Padding uint64
|
||||
}
|
||||
|
||||
type record struct {
|
||||
Count, Size uint64
|
||||
Pcs []uint64
|
||||
}
|
||||
|
||||
type trailer struct {
|
||||
Zero, One, Zeroh uint64
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s sample
|
||||
|
||||
r := io.Reader(os.Stdin)
|
||||
w := io.Writer(os.Stdout)
|
||||
|
||||
records := make(map[string]uint64, 16384)
|
||||
backtraces := make(map[string][]uint64, 1024)
|
||||
|
||||
/* ignore the documentation, it's wrong, first word must be zero.
|
||||
* the perl code that figures word length depends on it.
|
||||
*/
|
||||
hdr := hdr{0, 3, 0, 10000, 0}
|
||||
trailer := trailer{0, 1, 0}
|
||||
start := uint64(0)
|
||||
end := start
|
||||
nsamples := end
|
||||
for binary.Read(r, binary.LittleEndian, &s) == nil {
|
||||
numpcs := int(s.Wordcount)
|
||||
bt := make([]uint64, numpcs)
|
||||
binary.Read(r, binary.LittleEndian, &bt)
|
||||
//fmt.Printf("%v\n", bt)
|
||||
record := ""
|
||||
/* Fix the symbols. pprof was unhappy about the 0xfffffff.
|
||||
* N.B. The fact that we have to mess with the bt values
|
||||
* is the reason we did not write a stringer for bt.
|
||||
*/
|
||||
for i := range bt {
|
||||
bt[i] = bt[i] & ((uint64(1) << 32) - 1)
|
||||
record = record + fmt.Sprintf("0x%x ", bt[i])
|
||||
}
|
||||
records[record]++
|
||||
backtraces[record] = bt
|
||||
//fmt.Printf("%v %d %d %x %v record %v\n", s, s.Wordcount, s.Coreid, s.Ns, bt, record)
|
||||
/* how sad, once we go to ticks this gets ugly. */
|
||||
if start == 0 {
|
||||
start = s.Ns
|
||||
}
|
||||
end = s.Ns
|
||||
nsamples++
|
||||
}
|
||||
/* we'll need to fix this once we go to ticks. */
|
||||
hdr.Period = (end - start) / nsamples
|
||||
hdr.Count = uint64(0) // !@$@!#$!@#$len(records))
|
||||
//fmt.Printf("start %v end %v nsamples %d period %d\n", start, end, nsamples, hdr.Period)
|
||||
binary.Write(w, binary.LittleEndian, &hdr)
|
||||
out := make([]uint64, 2)
|
||||
/* note that the backtrace length varies. But we're good with that. */
|
||||
for key, v := range records {
|
||||
bt := backtraces[key]
|
||||
out[0] = v
|
||||
out[1] = uint64(len(bt))
|
||||
dump := append(out, bt...)
|
||||
//fmt.Printf("dump %v\n", dump)
|
||||
binary.Write(w, binary.LittleEndian, &dump)
|
||||
}
|
||||
binary.Write(w, binary.LittleEndian, &trailer)
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
/preen
|
|
@ -1,157 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var config = struct {
|
||||
Jehanne string
|
||||
Args []string
|
||||
Except map[string]bool
|
||||
CmdName string
|
||||
FullPath string
|
||||
Src string
|
||||
Uroot string
|
||||
Cwd string
|
||||
Bbsh string
|
||||
|
||||
Goroot string
|
||||
Gosrcroot string
|
||||
Arch string
|
||||
Goos string
|
||||
Gopath string
|
||||
TempDir string
|
||||
Go string
|
||||
Debug bool
|
||||
Fail bool
|
||||
}{
|
||||
Jehanne: os.Getenv("JEHANNE"),
|
||||
Except: map[string]bool{"cflags.json": true, "9": true, "libauthcmd.json": true, "awk.json": true, "bzip2": true, "klibc.json": true, "kcmds.json": true, "klib.json": true, "kernel.json": true},
|
||||
}
|
||||
|
||||
type fixup func(string, map[string]interface{})
|
||||
|
||||
func cflags(n string, jsmap map[string]interface{}) {
|
||||
if _, ok := jsmap["Cflags"]; ok {
|
||||
log.Printf("Deleting Cflags from %v", n)
|
||||
delete(jsmap, "Cflags")
|
||||
// TODO: once we have another ARCH, use it.
|
||||
a := []string{"/arch/amd64/include/cflags.json"}
|
||||
switch tval := jsmap["Include"].(type) {
|
||||
case []interface{}:
|
||||
for _, v := range tval {
|
||||
a = append(a, v.(string))
|
||||
}
|
||||
}
|
||||
jsmap["Include"] = a
|
||||
}
|
||||
}
|
||||
|
||||
func removeempty(n string, jsmap map[string]interface{}) {
|
||||
for key, val := range jsmap {
|
||||
switch tval := val.(type) {
|
||||
case map[string]interface{}:
|
||||
log.Printf("%s: tval %s", n, tval)
|
||||
if len(tval) == 0 {
|
||||
delete(jsmap, key)
|
||||
}
|
||||
case []interface{}:
|
||||
if len(tval) == 0 {
|
||||
delete(jsmap, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkname(n string, jsmap map[string]interface{}) {
|
||||
if _, ok := jsmap["Name"]; !ok {
|
||||
log.Print("File %v has no \"Name\" key", n)
|
||||
}
|
||||
}
|
||||
|
||||
func one(n string, f ...fixup) error {
|
||||
buf, err := ioutil.ReadFile(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var jsmap map[string]interface{}
|
||||
if err := json.Unmarshal(buf, &jsmap); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Debug {
|
||||
log.Printf("%v: %v", n, jsmap)
|
||||
}
|
||||
|
||||
for _, d := range f {
|
||||
d(n, jsmap)
|
||||
}
|
||||
buf, err = json.MarshalIndent(jsmap, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = append(buf, '\n')
|
||||
|
||||
if config.Debug {
|
||||
os.Stdout.Write(buf)
|
||||
} else {
|
||||
ioutil.WriteFile(n, buf, 0666)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if config.Jehanne == "" {
|
||||
log.Fatalf("Please set $JEHANNE")
|
||||
}
|
||||
|
||||
if len(config.Args) == 0 {
|
||||
config.Args = []string{config.Jehanne}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
flag.BoolVar(&config.Debug, "d", true, "Enable debug prints")
|
||||
flag.Parse()
|
||||
config.Args = flag.Args()
|
||||
for _, n := range config.Args {
|
||||
err = filepath.Walk(n, func(name string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
if config.Except[fi.Name()] {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
n := fi.Name()
|
||||
if len(n) < 5 || n[len(n)-5:] != ".json" {
|
||||
return nil
|
||||
}
|
||||
todo := []fixup{cflags, removeempty, checkname}
|
||||
if config.Except[n] {
|
||||
todo = todo[1:]
|
||||
}
|
||||
log.Printf("process %s", name)
|
||||
err = one(name, todo...)
|
||||
if err != nil {
|
||||
log.Printf("%s: %s\n", name, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("%v", err)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
Sz byte
|
||||
_ byte
|
||||
Core uint16
|
||||
_ uint16
|
||||
Ver byte
|
||||
Sig byte
|
||||
Time uint64
|
||||
}
|
||||
|
||||
type pc uint64
|
||||
|
||||
type Record struct {
|
||||
Header
|
||||
pcs []pc
|
||||
}
|
||||
|
||||
|
||||
type Symbol struct {
|
||||
Addr uint64
|
||||
Name string
|
||||
}
|
||||
|
||||
type Symbols []*Symbol
|
||||
|
||||
func (s Symbols) Len() int { return len(s) }
|
||||
func (s Symbols) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
type ByAddress struct{ Symbols }
|
||||
|
||||
func (s ByAddress) Less(i, j int) bool { return s.Symbols[i].Addr < s.Symbols[j].Addr }
|
||||
|
||||
var (
|
||||
profile = flag.String("profile", "", "name of file containing profile data")
|
||||
debug = flag.Bool("d", false, "Debug printing")
|
||||
kernel = flag.String("kernel", "", "kernel to profile against")
|
||||
symbolTable []*Symbol
|
||||
)
|
||||
|
||||
func loadSymbols(kernel *elf.File) {
|
||||
syms, err := kernel.Symbols()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, sym := range syms {
|
||||
value := sym.Value | 0xffffffff00000000
|
||||
symbolTable = append(symbolTable, &Symbol{value, sym.Name})
|
||||
}
|
||||
sort.Sort(ByAddress{symbolTable})
|
||||
}
|
||||
|
||||
func findFunction(pc pc) string {
|
||||
addr := uint64(pc)
|
||||
var prevSym *Symbol
|
||||
for _, sym := range symbolTable {
|
||||
if sym.Addr > addr && prevSym != nil {
|
||||
return prevSym.Name
|
||||
}
|
||||
prevSym = sym
|
||||
}
|
||||
return "*** Not Found ***"
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
kernelElf, err := elf.Open(*kernel)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
loadSymbols(kernelElf)
|
||||
|
||||
backtraces, err := ioutil.ReadFile(*profile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(backtraces)
|
||||
var numRecords int
|
||||
var records []Record
|
||||
for {
|
||||
var h Header
|
||||
if err := binary.Read(b, binary.LittleEndian, &h); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "header: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if *debug {
|
||||
fmt.Fprintf(os.Stderr, "%d: %v\n", numRecords, h)
|
||||
}
|
||||
|
||||
if h.Sig != 0xee {
|
||||
fmt.Fprintf(os.Stderr, "Record %d: sig is 0x%x, not 0xee", numRecords, h.Sig)
|
||||
os.Exit(1)
|
||||
}
|
||||
pcs := make([]pc, h.Sz)
|
||||
if err := binary.Read(b, binary.LittleEndian, pcs); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "data: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
numRecords++
|
||||
records = append(records, Record{Header: h, pcs: pcs})
|
||||
}
|
||||
|
||||
for _, r := range records {
|
||||
fmt.Printf("0x%x: ", r.Time)
|
||||
for _, pc := range r.pcs {
|
||||
fmt.Printf("[0x%x, %s] ", pc, findFunction(pc))
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var dry = flag.Bool("dryrun", true, "don't really do it")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
a := flag.Args()
|
||||
for _, n := range a {
|
||||
f, err := elf.Open(n)
|
||||
if err != nil {
|
||||
fmt.Printf("%v %v\n", n, err)
|
||||
continue
|
||||
}
|
||||
|
||||
s, err := f.Symbols()
|
||||
if err != nil {
|
||||
fmt.Printf("%v %v\n", n, err)
|
||||
continue
|
||||
}
|
||||
usem := false
|
||||
for _, v := range s {
|
||||
if v.Name == "m" && v.Section == elf.SHN_UNDEF {
|
||||
usem = true
|
||||
cf := strings.Split(n, ".")
|
||||
globs, err := filepath.Glob("../*/" + cf[0] + ".c")
|
||||
if err != nil || len(globs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%v has NO source?\n", cf[0])
|
||||
continue
|
||||
}
|
||||
if len(globs) > 1 {
|
||||
fmt.Fprintf(os.Stderr, "Skipping %v has more than one source?\n", cf[0])
|
||||
continue
|
||||
}
|
||||
file := globs[0]
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
continue
|
||||
}
|
||||
/* OK, read it in, write it out */
|
||||
b, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v: %v\n", file, err)
|
||||
}
|
||||
header := []byte("extern Mach *m; // REMOVE ME\n")
|
||||
if bytes.Compare(header[:], b[0:len(header)]) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%v already done; skipping\n", file)
|
||||
continue
|
||||
}
|
||||
out := append([]byte("typedef struct Mach Mach; extern Mach *m; // REMOVE ME\n"), b...)
|
||||
if *dry {
|
||||
fmt.Fprintf(os.Stderr, "Would do %v mode %v\n", file, fi.Mode())
|
||||
continue
|
||||
}
|
||||
if err := ioutil.WriteFile(file, out, fi.Mode()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Write %v failed: %v; git checkout %v\n", file, err, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !usem {
|
||||
fmt.Fprintf(os.Stderr, "Ignored %v as it did not reference Mach *m\n", n)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Procssed %v as it did reference Mach *m\n", n)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
// Run commands received from stdin in qemu
|
||||
//
|
||||
// -p prompt => prompt to expect (default "10.0.2.15#")
|
||||
//
|
||||
// ENVIRONMENT
|
||||
// Needed: JEHANNE
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"flag"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"github.com/creack/pty"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var prompt string
|
||||
flag.StringVar(&prompt, "p", "10.0.2.15#", "the prompt to expect")
|
||||
flag.Parse()
|
||||
jehanne := os.Getenv("JEHANNE")
|
||||
if jehanne == "" {
|
||||
fmt.Printf("usage: cat cmds.rc | runqemu [-p prompt]\n")
|
||||
fmt.Printf("error: missing $JEHANNE\n");
|
||||
os.Exit(1)
|
||||
}
|
||||
if terminal.IsTerminal(0) {
|
||||
fmt.Printf("usage: cat cmds.rc | runqemu [-p prompt]\n")
|
||||
fmt.Printf("error: runqemu is intended for automation, pipe commands in.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
qemuCmd := "cd $JEHANNE/arch/amd64/kern && $JEHANNE/hacking/QA.sh\n"
|
||||
qemuCmd = os.ExpandEnv(qemuCmd)
|
||||
|
||||
sh := exec.Command("/bin/sh")
|
||||
qemu, err := pty.Start(sh)
|
||||
if err != nil {
|
||||
fmt.Printf("REGRESS start (%s): %s", qemuCmd, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
qemu.WriteString(qemuCmd)
|
||||
defer qemu.Close()
|
||||
|
||||
exitStatus := 0
|
||||
|
||||
qemuInput := make(chan string)
|
||||
qemuOutputRaw := make(chan string)
|
||||
wait := make(chan int)
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
go func() {
|
||||
qemuOut := make([]byte, 256)
|
||||
for {
|
||||
r, err := qemu.Read(qemuOut)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
wait <- 3
|
||||
}
|
||||
qemuOutputRaw <- string(qemuOut[0:r])
|
||||
}
|
||||
}()
|
||||
go func(){
|
||||
line := ""
|
||||
for {
|
||||
s := <- qemuOutputRaw
|
||||
line += s
|
||||
if strings.Contains(line, prompt) {
|
||||
if scanner.Scan() {
|
||||
cmd := scanner.Text()
|
||||
qemuInput <- fmt.Sprintf("%s\n", cmd)
|
||||
} else {
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
wait <- 4
|
||||
} else {
|
||||
wait <- exitStatus
|
||||
}
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s", line)
|
||||
line = ""
|
||||
} else if strings.ContainsAny(line, "\r\n") {
|
||||
if strings.Contains(line, "FAIL") {
|
||||
exitStatus = 5
|
||||
}
|
||||
fmt.Printf("%s", line)
|
||||
line = ""
|
||||
}
|
||||
}
|
||||
}()
|
||||
go func(){
|
||||
for {
|
||||
s := <- qemuInput
|
||||
i := 0;
|
||||
for {
|
||||
n, err := qemu.WriteString(s[i:])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
wait <- 6
|
||||
}
|
||||
i += n
|
||||
if i == len(s) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
e := <- wait
|
||||
if e == 0 {
|
||||
fmt.Printf("\nDone.\n")
|
||||
}
|
||||
os.Exit(e)
|
||||
}
|
|
@ -1,244 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Copyright 2012 the u-root Authors. All rights reserved
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
type (
|
||||
chunk struct {
|
||||
id int
|
||||
buf []byte
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
echo = false
|
||||
)
|
||||
|
||||
func comprefix(lines [][]byte) (int, int) {
|
||||
var line0 []byte
|
||||
len0 := 0
|
||||
for _, ln := range lines {
|
||||
if len(ln) > len0 {
|
||||
line0 = ln
|
||||
len0 = len(ln)
|
||||
}
|
||||
}
|
||||
ndiff := 0
|
||||
for i := range line0 {
|
||||
nshort := 0
|
||||
for _, ln := range lines {
|
||||
if i >= len(ln) {
|
||||
nshort++
|
||||
} else if ln[i] != line0[i] {
|
||||
ndiff++
|
||||
}
|
||||
}
|
||||
if ndiff > 0 {
|
||||
return i, ndiff
|
||||
}
|
||||
if nshort > 0 {
|
||||
return i, ndiff
|
||||
}
|
||||
}
|
||||
return len(line0), ndiff
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var conns []*net.TCPConn
|
||||
for _, a := range os.Args[1:] {
|
||||
port := "1522"
|
||||
if !strings.Contains(a, ":") {
|
||||
a = a + ":" + port
|
||||
}
|
||||
|
||||
tcpdst, err := net.ResolveTCPAddr("tcp", a)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c, err := net.DialTCP("tcp", nil, tcpdst)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
conns = append(conns, c)
|
||||
}
|
||||
|
||||
if terminal.IsTerminal(0) {
|
||||
unraw, err := terminal.MakeRaw(0)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer terminal.Restore(0, unraw)
|
||||
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
go func() {
|
||||
for _ = range sigc {
|
||||
terminal.Restore(0, unraw)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, 256)
|
||||
for {
|
||||
n, err := os.Stdin.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
for _, c := range conns {
|
||||
c.CloseWrite() // I know, I know.. but it is handy sometimes.
|
||||
}
|
||||
break
|
||||
}
|
||||
buf = buf[0:n]
|
||||
for i := range buf {
|
||||
if echo {
|
||||
fmt.Printf("%s", string(buf[i]))
|
||||
}
|
||||
if buf[i] == '\r' {
|
||||
buf[i] = '\n'
|
||||
if echo {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range conns {
|
||||
if _, err := c.Write(buf); err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
waitc := make(chan int)
|
||||
chunkc := make(chan chunk)
|
||||
for id, c := range conns {
|
||||
go func(id int, c *net.TCPConn) {
|
||||
for {
|
||||
buf := make([]byte, 256)
|
||||
n, err := c.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
c.Close()
|
||||
waitc <- 1
|
||||
break
|
||||
}
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
chunkc <- chunk{id, buf[0:n]}
|
||||
}
|
||||
}(id, c)
|
||||
}
|
||||
|
||||
act := make([][]byte, len(conns))
|
||||
|
||||
go func() {
|
||||
oldpfix := 0
|
||||
insync := true
|
||||
for {
|
||||
chunk, more := <-chunkc
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
id := chunk.id
|
||||
for _, b := range chunk.buf {
|
||||
act[id] = append(act[id], b)
|
||||
if false && b == '\b' {
|
||||
act[id] = append(act[id], ' ')
|
||||
act[id] = append(act[id], '\b')
|
||||
}
|
||||
}
|
||||
|
||||
/* streams agree, so spit out all they agree on */
|
||||
if insync {
|
||||
newpfix, ndiff := comprefix(act)
|
||||
if ndiff == 0 && newpfix > oldpfix {
|
||||
os.Stdout.Write(act[id][oldpfix:newpfix])
|
||||
for {
|
||||
i := bytes.IndexByte(act[id][:newpfix], '\n')
|
||||
if i == -1 {
|
||||
break
|
||||
}
|
||||
for id := range act {
|
||||
clen := copy(act[id], act[id][i+1:])
|
||||
act[id] = act[id][0:clen]
|
||||
}
|
||||
newpfix = newpfix - (i + 1)
|
||||
}
|
||||
oldpfix = newpfix
|
||||
}
|
||||
|
||||
if ndiff > 0 {
|
||||
insync = false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* streams disagree, first to newline completes from oldpfix on.
|
||||
* the outer loop is just for transitioning from insync, where
|
||||
* a leader may be several lines ahead
|
||||
*/
|
||||
if !insync {
|
||||
for id := range act {
|
||||
for {
|
||||
i := bytes.IndexByte(act[id], '\n')
|
||||
if i != -1 {
|
||||
if oldpfix == 0 {
|
||||
fmt.Fprintf(os.Stdout, "[%d]", id)
|
||||
}
|
||||
os.Stdout.Write(act[id][oldpfix : i+1])
|
||||
clen := copy(act[id], act[id][i+1:])
|
||||
act[id] = act[id][0:clen]
|
||||
oldpfix = 0
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* try for prompt (or sheer luck) */
|
||||
if _, ndiff := comprefix(act); ndiff == 0 {
|
||||
insync = true
|
||||
oldpfix = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
waitc <- 1
|
||||
}()
|
||||
|
||||
for _ = range conns {
|
||||
<-waitc
|
||||
}
|
||||
close(chunkc)
|
||||
<-waitc
|
||||
}
|
|
@ -1,272 +0,0 @@
|
|||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016-2019 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const gplHeader string = `/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016-2019 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* automatically generated by usyscalls */
|
||||
`
|
||||
|
||||
type SyscallConf struct {
|
||||
Ret []string
|
||||
Args []string
|
||||
Name string
|
||||
Id uint32
|
||||
}
|
||||
|
||||
type Sysconf struct {
|
||||
Syscalls []SyscallConf
|
||||
}
|
||||
|
||||
type SyscallWrapper struct {
|
||||
Id uint32
|
||||
Name string
|
||||
FuncArgs string
|
||||
MacroArgs string
|
||||
VarValues []string
|
||||
Vars []string
|
||||
AsmArgs string
|
||||
AsmClobbers string
|
||||
RetType string
|
||||
}
|
||||
|
||||
type HeaderCode struct {
|
||||
Wrappers []SyscallWrapper
|
||||
}
|
||||
|
||||
|
||||
func usage(msg string) {
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
fmt.Fprint(os.Stderr, "Usage: usyscalls header|code path/to/sysconf.json\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func argTypeName(t string) string{
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return "i"
|
||||
case "unsigned int", "uint32_t":
|
||||
/* unsigned int is reserved for flags */
|
||||
return "ui"
|
||||
case "long", "int64_t":
|
||||
return "l"
|
||||
case "unsigned long", "uint64_t":
|
||||
return "ul"
|
||||
case "void*", "uint8_t*", "const void*", "const uint8_t*":
|
||||
return "p"
|
||||
case "int32_t*", "int*", "const int32_t*", "const int*":
|
||||
return "p"
|
||||
case "const char*", "char*":
|
||||
return "p"
|
||||
case "const char**", "char**":
|
||||
return "p"
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
}
|
||||
|
||||
func argRegister(index int, t string) string{
|
||||
prefix := ""
|
||||
switch(t){
|
||||
case "int", "unsigned int", "uint32_t", "int32_t":
|
||||
prefix = "e"
|
||||
default:
|
||||
prefix = "r"
|
||||
}
|
||||
switch(index){
|
||||
case 0:
|
||||
return prefix + "di";
|
||||
case 1:
|
||||
return prefix + "si";
|
||||
case 2:
|
||||
return prefix + "dx";
|
||||
case 3:
|
||||
return "r10";
|
||||
case 4:
|
||||
return "r8";
|
||||
case 5:
|
||||
return "r9";
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getHeaderData(calls []SyscallConf) *HeaderCode {
|
||||
code := new(HeaderCode)
|
||||
for _, call := range(calls) {
|
||||
wcall := new(SyscallWrapper)
|
||||
wcall.Id = call.Id
|
||||
wcall.Name = call.Name
|
||||
wcall.RetType = call.Ret[0]
|
||||
|
||||
clobberMemory := false
|
||||
wcall.AsmClobbers = "\"cc\", \"rcx\", \"r11\""
|
||||
wcall.AsmArgs = fmt.Sprintf("\"0\"(%d)", wcall.Id)
|
||||
for i, a := range(call.Args){
|
||||
typeName := argTypeName(a)
|
||||
if i > 0 {
|
||||
wcall.FuncArgs += ", "
|
||||
wcall.MacroArgs += ", "
|
||||
}
|
||||
if strings.HasSuffix(a, "*") && !strings.HasPrefix(a, "const"){
|
||||
clobberMemory = true
|
||||
}
|
||||
wcall.FuncArgs += fmt.Sprintf("%s a%d", a, i)
|
||||
wcall.MacroArgs += fmt.Sprintf("/* %s */ a%d", a, i)
|
||||
if typeName == "p" {
|
||||
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = ((volatile void*)(a%d)); \\\n\t", i, typeName, i))
|
||||
} else {
|
||||
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = (a%d); \\\n\t", i, typeName, i))
|
||||
}
|
||||
wcall.Vars = append(wcall.Vars, fmt.Sprintf("register %s __r%d asm(\"%s\") = _sysargs[%d].%s; \\\n\t", a, i, argRegister(i, a), i, argTypeName(a)))
|
||||
wcall.AsmArgs += fmt.Sprintf(", \"r\"(__r%d)", i)
|
||||
}
|
||||
if clobberMemory {
|
||||
wcall.AsmClobbers += ", \"memory\""
|
||||
}
|
||||
code.Wrappers = append(code.Wrappers, *wcall)
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
func generateSyscallTable(calls []SyscallConf){
|
||||
code := getHeaderData(calls)
|
||||
tmpl, err := template.New("tab.c").Parse(`
|
||||
{{ range .Wrappers }}"{{ .Name }}", (int(*)()) sys_{{ .Name }},
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func generateLibcCode(calls []SyscallConf){
|
||||
code := getHeaderData(calls)
|
||||
tmpl, err := template.New("syscalls.c").Parse(gplHeader + `
|
||||
#define PORTABLE_SYSCALLS
|
||||
#include <u.h>
|
||||
|
||||
{{ range .Wrappers }}
|
||||
#pragma weak sys_{{ .Name }}
|
||||
{{ .RetType }}
|
||||
sys_{{ .Name }}({{ .FuncArgs }})
|
||||
{
|
||||
register {{ .RetType }} __ret asm ("rax");
|
||||
__asm__ __volatile__ (
|
||||
"movq %%rcx, %%r10" "\n\t"
|
||||
"movq ${{ .Id }}, %%rax" "\n\t"
|
||||
"syscall"
|
||||
: "=r" (__ret)
|
||||
: /* args are ready */
|
||||
: {{ .AsmClobbers }}
|
||||
);
|
||||
return __ret;
|
||||
}
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func generateSyscallHeader(calls []SyscallConf){
|
||||
funcMap := template.FuncMap{
|
||||
"title": strings.Title,
|
||||
}
|
||||
code := getHeaderData(calls)
|
||||
tmpl, err := template.New("syscall.h").Funcs(funcMap).Parse(gplHeader + `
|
||||
typedef enum Syscalls
|
||||
{
|
||||
{{ range .Wrappers }} Sys{{ .Name|title }} = {{ .Id }},
|
||||
{{ end }}} Syscalls;
|
||||
|
||||
#ifndef KERNEL
|
||||
|
||||
{{ range .Wrappers }}extern {{ .RetType }} sys_{{ .Name }}({{ .FuncArgs }});
|
||||
{{ end }}
|
||||
|
||||
#endif
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 2 {
|
||||
usage("no path to sysconf.json")
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(flag.Arg(1))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var sysconf Sysconf
|
||||
err = json.Unmarshal(buf, &sysconf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
mode := flag.Arg(0)
|
||||
switch(mode){
|
||||
case "header":
|
||||
generateSyscallHeader(sysconf.Syscalls)
|
||||
break
|
||||
case "code":
|
||||
generateLibcCode(sysconf.Syscalls)
|
||||
break
|
||||
case "tab":
|
||||
generateSyscallTable(sysconf.Syscalls)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
Vendor is a tool to vendor software for Jehanne.
|
||||
|
||||
It downloads a tarball, verifies it against supplied hashes, extracts it
|
||||
into "upstream", modifies all the files to be read-only, and then commits
|
||||
the results.
|
||||
|
||||
When invoked with the flag `-check` it verify that the files present in
|
||||
a previously vendorized "upstream" folder match those in the downloaded tarball.
|
||||
|
||||
Vendor is purposely unhelpful and un-customisable.
|
||||
|
||||
VENDORFILE
|
||||
|
||||
It requires a "vendor.json" file in the current directory with the following
|
||||
structure:
|
||||
|
||||
{
|
||||
"Upstream":"",
|
||||
"Digest": {
|
||||
"":""
|
||||
},
|
||||
"Compress":"",
|
||||
"RemovePrefix": true,
|
||||
"Exclude": [
|
||||
""
|
||||
]
|
||||
}
|
||||
|
||||
"Upstream" is the URL to fetch a tarball from.
|
||||
|
||||
"Digest" is a map of algorithm-hash pairs for calculating checksums. All
|
||||
of the sha functions in the go standard library are supported except for sha1.
|
||||
The hash is hex-encoded, just like sha*sum output.
|
||||
|
||||
"Compress" is the compression type of the tarball. Gzip and bzip are
|
||||
supported. If this key is omitted, the tarball is assumed to be uncompressed.
|
||||
|
||||
"RemovePrefix" is a boolean toggle for if the first element of files in the
|
||||
archive should be removed. Defaults to false if omitted.
|
||||
|
||||
"Exclude" is an array of prefix strings for files that should not be
|
||||
extracted. They are used as literal prefixes and not interpreted in any way.
|
||||
*/
|
||||
package main
|
|
@ -1,248 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ignore = "*\n!.gitignore\n"
|
||||
dirPermissions = 0755
|
||||
)
|
||||
|
||||
type V struct {
|
||||
Upstream string
|
||||
Digest map[string]string
|
||||
Compress string
|
||||
RemovePrefix bool
|
||||
Exclude []string
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||
|
||||
justCheck := flag.Bool("check", false, "verify the code in upstream/")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if(*justCheck && !repositoryIsClean()){
|
||||
log.Fatal("cannot verify upstream/ files: working directory not clean")
|
||||
}
|
||||
|
||||
f, err := ioutil.ReadFile("vendor.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
vendor := &V{}
|
||||
if err := json.Unmarshal(f, vendor); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat("upstream"); err == nil {
|
||||
log.Println("recreating upstream")
|
||||
if(*justCheck){
|
||||
run("rm", "-r", "-f", "upstream")
|
||||
} else {
|
||||
run("git", "rm", "-r", "-f", "upstream")
|
||||
}
|
||||
} else {
|
||||
if(*justCheck){
|
||||
log.Fatalf("Cannot verify upstream/ as it does not exists.")
|
||||
}
|
||||
os.MkdirAll("patch", dirPermissions)
|
||||
os.MkdirAll("jehanne", dirPermissions)
|
||||
ig, err := os.Create(path.Join("jehanne", ".gitignore"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer ig.Close()
|
||||
if _, err := ig.WriteString(ignore); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
run("git", "add", ig.Name())
|
||||
}
|
||||
|
||||
if err := do(vendor, *justCheck); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if(*justCheck){
|
||||
if(repositoryIsClean()){
|
||||
log.Printf("the files in upstream/ matches those in "+path.Base(vendor.Upstream))
|
||||
} else {
|
||||
log.Fatalf("the files in upstream/ does not match those in "+path.Base(vendor.Upstream))
|
||||
}
|
||||
} else {
|
||||
run("git", "add", "vendor.json")
|
||||
run("git", "commit", "-s", "-m", "vendor: pull in "+path.Base(vendor.Upstream))
|
||||
}
|
||||
}
|
||||
|
||||
func repositoryIsClean() bool {
|
||||
out, err := exec.Command("git", "status", "--porcelain").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if(len(out) > 0){
|
||||
log.Println("git status --porcelain");
|
||||
log.Println(string(out))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func do(v *V, justCheck bool) error {
|
||||
name := fetch(v)
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(name)
|
||||
|
||||
var unZ io.Reader
|
||||
switch v.Compress {
|
||||
case "gzip":
|
||||
unZ, err = gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "bzip2":
|
||||
unZ = bzip2.NewReader(f)
|
||||
default:
|
||||
unZ = f
|
||||
}
|
||||
|
||||
ar := tar.NewReader(unZ)
|
||||
h, err := ar.Next()
|
||||
untar:
|
||||
for ; err == nil; h, err = ar.Next() {
|
||||
n := h.Name
|
||||
if v.RemovePrefix {
|
||||
n = strings.SplitN(n, "/", 2)[1]
|
||||
}
|
||||
for _, ex := range v.Exclude {
|
||||
if strings.HasPrefix(n, ex) {
|
||||
continue untar
|
||||
}
|
||||
}
|
||||
n = path.Join("upstream", n)
|
||||
if h.FileInfo().IsDir() {
|
||||
os.MkdirAll(n, dirPermissions)
|
||||
continue
|
||||
}
|
||||
os.MkdirAll(path.Dir(n), dirPermissions)
|
||||
out, err := os.Create(n)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if n, err := io.Copy(out, ar); n != h.Size || err != nil {
|
||||
return err
|
||||
}
|
||||
out.Close()
|
||||
}
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
if(justCheck){
|
||||
return nil;
|
||||
}
|
||||
return run("git", "add", "upstream")
|
||||
}
|
||||
|
||||
type match struct {
|
||||
hash.Hash
|
||||
Good []byte
|
||||
Name string
|
||||
}
|
||||
|
||||
func (m match) OK() bool {
|
||||
return bytes.Equal(m.Good, m.Hash.Sum(nil))
|
||||
}
|
||||
|
||||
func fetch(v *V) string {
|
||||
if len(v.Digest) == 0 {
|
||||
log.Fatal("no checksums specifed")
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "cmdVendor")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
req, err := http.NewRequest("GET", v.Upstream, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DisableCompression: true,
|
||||
},
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var digests []match
|
||||
for k, v := range v.Digest {
|
||||
g, err := hex.DecodeString(v)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
switch k {
|
||||
case "sha224":
|
||||
digests = append(digests, match{sha256.New224(), g, k})
|
||||
case "sha256":
|
||||
digests = append(digests, match{sha256.New(), g, k})
|
||||
case "sha384":
|
||||
digests = append(digests, match{sha512.New384(), g, k})
|
||||
case "sha512":
|
||||
digests = append(digests, match{sha512.New(), g, k})
|
||||
}
|
||||
}
|
||||
ws := make([]io.Writer, len(digests))
|
||||
for i := range digests {
|
||||
ws[i] = digests[i]
|
||||
}
|
||||
w := io.MultiWriter(ws...)
|
||||
|
||||
if _, err := io.Copy(f, io.TeeReader(res.Body, w)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, h := range digests {
|
||||
if !h.OK() {
|
||||
log.Fatalf("mismatched %q hash\n\tWanted %x\n\tGot %x\n", h.Name, h.Good, h.Hash.Sum(nil))
|
||||
}
|
||||
}
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
func run(exe string, arg ...string) error {
|
||||
cmd := exec.Command(exe, arg...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 607401678236b608280b291ad849a109b8d9a8f2
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1d47bf91a6d4828038e9fbd11508c90fc983dce0
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d65923fd17e8b158350d3ccd6a4e32b89b15014a
|
|
@ -1 +0,0 @@
|
|||
Subproject commit aed6e1e7aa37744dba150fa2974f322813d23478
|
Loading…
Reference in New Issue