#!/bin/bash
#
# Install CML2 in an existing tree.  This script has a couple of properties
# worth noting:
#
# 1. It modifies only the top-level Makefile of the tree.  It will not touch
#    any other files in the tree -- in fact, it will abort rather than step 
#    on any other files.
#
# 2. It can be applied multiple times.  Each time, it undoes the results 
#    of the previous installation before reinstalling.
#
# 3. You can give it the root of a Linux source tree as a command-line argument
#    to short-circuit its normal behavior of searching for source trees and
#    prompting to get one of these.
#
# 4. You can uninstall by giving the -u switch, either with or without a
#    source tree name.
#

echo "Examining your build environment..."
python=""
for ver in '2.2' '2.1' '2' ''
do
    try=`python$ver -c "import sys; print sys.version[0]" 2>/dev/null`
    if [ $? = 0 -a "$try" = 2 ]
    then
	echo "Good. You have Python 2.x installed as 'python$ver' already."
	python=python$ver
	break
    fi
done

if [ "$python" = "" ]
then
    echo "You need to install Python 2.0.  See http://www.python.org."
    exit 1
fi

if  [ "$1" = "-u" ]
then
    uninstall=1
    root=$2
else
    uninstall=0
    root=$1
fi

if [ "$root" = "" ]
then
    cat <<EOF
Looking for Linux source trees on your system -- using the locate database,
so newer trees might not be found until the database is updated.  You can
override this by giving the tree root as a command line argument to
install-cml2.

(Note: If your updatedb database is out of date, this script may bomb.)

EOF
    sourcetrees=`locate Documentation/Configure.help | grep 'help$' | grep -v kernel-tree`
    if [ $? = 127 ]
    then
	echo "Uh oh. Can't run 'locate'.  What kind of Linux system is this?"
    fi
fi

$python - $python <<EOF
import sys, os, re, commands
print "Python looks sane..."

if sys.version[0] < '2':
    print "Python 2.0 or later is required for CML2."
    sys.exit(0)

try:
    import curses
    print "Good, your python has curses support linked in."
except ImportError:
    print "Your Python doesn't have curses support linked in; 'menuconfig' will fail." 

try:
    from Tkinter import *
    print "Good, your python has Tk support linked in."
except ImportError:
    print "Your Python doesn't have Tk support linked in; 'xconfig' will fail."

root='$root'
tree=os.path.abspath(root)
uninstall=$uninstall

if not root:
    sourcetrees="""$sourcetrees"""
    if sourcetrees:
	sourcetrees = map(lambda x: x[:-28], sourcetrees.split('\n'))
	print "I see source trees at " + (" and ".join(sourcetrees)) + "." 
    else:
	print "There don't appear to be any Linux source trees here."
	raise SystemExit

    if len(sourcetrees) == 1:
	tree = sourcetrees[0]
    else:
	newstdin = open("/dev/tty")
	while 1:
	    print "Please pick a source tree to operate on:"
	    i = 1
	    for t in sourcetrees:
		print "%d: %s" % (i, t)
		i += 1
	    sys.stdout.write("Which one? ")
	    num = newstdin.readline()
	    try:
		num = int(num)
		if num >= i:
		    raise ValueError
	    except:
		print "Eh? I can't interpret that"
	    else:
		tree = sourcetrees[num - 1]
		break
	newstdin.close()

if not os.access(tree, os.F_OK):
    print tree, "doesn't seem to exist."
    raise SystemExit

if not os.access(tree, os.W_OK):
    print "You don't have write permissions for", tree
    raise SystemExit

print "Compiling file list..."
filelist = commands.getoutput("cd kernel-tree >/dev/null; find * -type f -print | grep -v RCS").split('\n')

print "Operating on %s..." % tree

# Remove any old installation, if present
#helpfile = os.path.join(tree,"Documentation/Configure.help")
if os.path.exists(os.path.join(tree, "rules.cml")):
    print "I see you have installed CML2 here before."
    print "Cleaning up old CML2 files..."
    for file in filelist:
	path = os.path.join(tree, file)
	if os.path.exists(path):
	    os.remove(path)
    try:
	os.rename(
            os.path.join(tree,"Makefile.bak"),
            os.path.join(tree,"Makefile"))
    except OSError:
        print "Error while renaming Makefile.bak"
    cml2dir = os.path.join(tree, "cml2")
    os.system("rm -fr %s" % cml2dir)
#    if os.path.exists(helpfile + ".bak"):
#	try:
#	    os.remove(helpfile)
#	    os.rename(helpfile + ".bak", helpfile)
#	except OSError:
#	    print "Error while restoring Documentation/Configure.help.bak"

# If all the user wanted was an uninstall, we're done
if uninstall:
    try:
	os.remove(os.path.join(tree, "config.out"))
	os.remove(os.path.join(tree, "rules.out"))
	os.remove(os.path.join(tree, "xref.out"))
    except OSError:
        pass
    print "Un-install complete."
    raise SystemExit, 0

# Check to see we're not stepping on anything
conflicts = 0
for file in filelist:
    if os.path.exists(os.path.join(tree, file)):
	print "Target tree already has", file
	conflicts += 1
if conflicts:
    print "Installation wouldn't be safe"
    raise SystemExit

# The actual installation
print "Installing new files..."
if os.system("(cd kernel-tree >/dev/null && tar --exclude '*.help' --exclude '*,v' --exclude '*~' --exclude '*.orig' --exclude '*.rej' --exclude '*.out' -chf - *) | (cd " + tree + " >/dev/null && tar -xf -)"):
    print "Huh?  Installation failed."
    print "Do have write permissions on the target directory " + tree + "?"
    raise SystemExit, 1

# May be time to patch the top-level Makefile
os.chdir(tree)

# We don't want to hack the top-level Makefile if this source tree has
# Keith Owens's kbuild-2.5 installed as the default build system.
# If there is a Makefile.in and is *not* a Makefile-2.5, we take this
# to have happened.
if not os.path.exists("Makefile.in") or os.path.exists("Makefile-2.5"):
    mfp = open("Makefile")
    contents = mfp.read()
    mfp.close()

    maketrailer = """
# CML2 productions begin

ARCHSYMBOL = \$(shell echo \$(ARCH) | \
	sed -e s/i.86/x86/ \
	-e s/sparc\$\$/sparc32/ \
	-e s/mips\$\$/mips32/ \
	-e s/sh\$\$/superh/ \
	| tr "[a-z]" "[A-Z]")

# In RH >= 7.2 and recent Debian versions, the python2.0 binary is named python2
PYTHON2 = \$(shell if [ -x "\$\$(which python2 2> /dev/null)" ]; then echo python2;else echo python; fi)

CONFIGURE = \$(PYTHON2) -O cml2/cmlconfigure.py
CONFIGPOST = -D\$(ARCHSYMBOL) -B \$(KERNELRELEASE) -i config.out \$(CONFIGOPTS) rules.out

rules.out: \$(shell find . -name "*cml")
	\$(PYTHON2) -O cml2/cmlcompile.py rules.cml 

# Run the autoconfigurator in normal mode; intended to be followed by *config.
autoprobe: symlinks rules.out
	\$(PYTHON2) -O cml2/autoconfigure.py -D\$(ARCHSYMBOL) %s --rules=cml2/autoconfigure.rules \$(CONFIGOPTS) >config.out
	echo '\$\$\$\$freeze' >>config.out
	make cml2-to-cml1

# Run the autoconfigurator in standalone mode
autoconfig: symlinks rules.out
	\$(PYTHON2) -O cml2/autoconfigure.py -D\$(ARCHSYMBOL) %s --standalone --rules=cml2/autoconfigure.rules \$(CONFIGOPTS) >config.out
	make cml2-to-cml1

# Make autoconf.h and .config from config.out
cml2-to-cml1: 
	\$(PYTHON2) -O cml2/configtrans.py -h include/linux/autoconf.h -s .config config.out

# Make config.out from defconfig and config
cml1-to-cml2:
	@-if [ -f .config -a ! -f config.out ]; then \$(PYTHON2) -O cml2/configtrans.py -t <.config >config.out; echo "Using .config"; fi
	@-if [ ! -f config.out ]; then cp arch/\$(ARCH)/defconfig config.out; echo "Using defconfig"; fi

# Configure with most visual UI available 
config: symlinks rules.out
	make cml1-to-cml2
	\$(CONFIGURE) %s \$(CONFIGPOST)
	make cml2-to-cml1

# Configure with lineoriented UI
ttyconfig: symlinks rules.out
	make cml1-to-cml2
	\$(CONFIGURE) -t %s \$(CONFIGPOST)
	make cml2-to-cml1

# Configure with screen-oriented UI
menuconfig: symlinks rules.out
	make cml1-to-cml2
	\$(CONFIGURE) -c %s \$(CONFIGPOST)
	make cml2-to-cml1

# Configure with basic X interface
xconfig: symlinks rules.out
	make cml1-to-cml2
	\$(CONFIGURE) -x %s \$(CONFIGPOST)
	make cml2-to-cml1

# Configure with alternate tree-widget-based X interface
treeconfig: symlinks rules.out
	make cml1-to-cml2
	\$(CONFIGURE) -q %s \$(CONFIGPOST)
	make cml2-to-cml1

# Use your existing configuration
oldconfig: symlinks rules.out
	make cml1-to-cml2
	\$(CONFIGURE) -t -D\$(ARCHSYMBOL) -B \$(KERNELRELEASE) -I config.out \$(CONFIGOPTS) rules.out
	make cml2-to-cml1

# Configuration -- it's not just a job, it's an adventure game!
advent: symlinks rules.out
	make cml1-to-cml2
	\$(PYTHON2) -O cml2/cmladvent.py -t \$(CONFIGPOST)
	make cml2-to-cml1

menu-map: rules.out
	\$(CONFIGURE) -l \$(CONFIGOPTS) rules.out >menu-map 2>&1

# CML2 documentation -- these require docbook-tools and jade.

cml2-paper.html: cml2-paper.sgml
	docbook2html --nochunks cml2-paper.sgml >cml2-paper.html

cml2-reference.html: cml2-reference.sgml
	docbook2html --nochunks cml2-reference.sgml >cml2-reference.html

cml2-paper.ps: cml2-paper.sgml
	docbook2ps cml2-paper.sgml

cml2-reference.ps: cml2-reference.sgml
	docbook2ps cml2-reference.sgml

# CML2 productions end
"""

    if not os.access("Makefile", os.F_OK):
	print "The top-level Makefile doesn't seem to exist."
	raise SystemExit
    else:
	print "Modifying configuration productions..."
	os.system("cp Makefile Makefile.bak; chmod u+w Makefile")
	contents = re.compile("\nconfig:").sub("\nconfig-old:", contents)
	contents = re.compile("\noldconfig:").sub("\noldconfig-old:", contents)
	contents = re.compile("\nmenuconfig:").sub("\nmenuconfig-old:", contents)
	contents = re.compile("\nxconfig:").sub("\nxconfig-old:", contents)
	contents = re.compile("\nMRPROPER_FILES =").sub("\nMRPROPER_FILES = config.out rules.out", contents)

	if tree.find("dj") > -1:
	    print "Installing for Dave Jones's tree"
	    devel = "-DJONES_TREE"
	elif tree.find("2.5") > -1:
	    print "Installing for Linus Torvalds's tree"
	    devel = "-DTORVALDS_TREE"
	elif tree.find("2.4") > -1:
	    print "Installing for Marcelo Tosatti's tree"
	    devel = "-DTOSATTI_TREE"
	else:
	    print "Warning: I can't tell whose tree this is."
	    devel = ""
	contents += maketrailer % ((devel,) * 7) 

	mfp = open("Makefile", "w")
	mfp.write(contents)
	mfp.close()

print "You are ready to go, cd to %s." % tree
EOF

