#!/usr/bin/env python3 import os import shutil import sys import platform import subprocess import tempfile prefix = '/usr/local' clean = True darwin = platform.system() == 'Darwin' def hostsanitize(host): host = host.split('-')[0] host = ''.join(c for c in host if c in '_0123456789abcdefghijklmnopqrstuvwxyz') for prefix,result in ( ('amd64','amd64'), ('x86_64','amd64'), ('x86','x86'), ('i386','x86'), ('i686','x86'), ('arm64','arm64'), ('armv8','arm64'), ('aarch64','arm64'), ('arm','arm32'), ('riscv64','riscv64'), ('riscv','riscv32'), ('mips64','mips64'), ('mips','mips32'), ('ppc64','ppc64'), ('powerpc64','ppc64'), ('powerpc','ppc32'), ('ppc','ppc32'), ('sparc64','sparc64'), ('sparcv9','sparc64'), ('sun4u','sparc64'), ('sparc','sparc32'), ('sun','sparc32'), ): if host.startswith(prefix): return result return host host = hostsanitize(platform.machine()) makefile = '' for arg in sys.argv[1:]: if arg.startswith('--prefix='): prefix = arg[9:] continue if arg.startswith('--host='): host = hostsanitize(arg[7:]) continue if arg == '--clean': clean = True continue if arg == '--noclean': clean = False continue if arg == '--darwin': darwin = True continue if arg == '--nodarwin': darwin = False continue raise ValueError('unrecognized argument %s' % arg) echoargs = './configure' echoargs += ' --prefix=%s' % prefix echoargs += ' --host=%s' % host if clean: echoargs += ' --clean' if not clean: echoargs += ' --noclean' if darwin: echoargs += ' --darwin' if not darwin: echoargs += ' --nodarwin' print(echoargs) if prefix[0] != '/': raise ValueError('prefix %s is not an absolute path' % prefix) rpath = None # XXX: rpath = '%s/lib' % prefix if clean: shutil.rmtree('build/%s' % host,ignore_errors=True) def dirlinksym(dir,source,target): with tempfile.TemporaryDirectory(dir=dir) as t: os.symlink(target,'%s/symlink' % t) os.rename('%s/symlink' % t,'%s/%s' % (dir,source)) os.makedirs('build/%s' % host,exist_ok=True) os.makedirs('build/%s/package/bin' % host,exist_ok=True) os.makedirs('build/%s/package/lib' % host,exist_ok=True) os.makedirs('build/%s/package/include' % host,exist_ok=True) if clean: os.symlink('../..','build/%s/src' % host) # ----- build scripts os.makedirs('build/%s/scripts'%host,exist_ok=True) dirlinksym('build/%s/scripts'%host,'install','../src/scripts-build/install') # ----- shared-library variations so = 'dylib' if darwin else 'so' so1 = '1.dylib' if darwin else 'so.1' soname = 'install_name' if darwin else 'soname' syslibs = '-lm' if darwin else '-lm -lrt' # ----- compilers def compilerversion(c): try: p = subprocess.Popen(c.split()+['--version'],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=True) out,err = p.communicate() assert not err assert not p.returncode return out except: pass firstcompiler = None with open('compilers/default') as f: for c in f.readlines(): c = c.strip() cv = compilerversion(c) if cv == None: print('skipping default compiler %s' % c) continue print('using default compiler %s' % c) firstcompiler = c break if firstcompiler is None: raise ValueError('did not find a working compiler') with open('build/%s/scripts/compiledefault' % host,'w') as f: f.write('#!/bin/sh\n') f.write('\n') f.write('dir="$1"; shift\n') f.write('base="$1"; shift\n') f.write('ext="$1"; shift\n') f.write('\n') f.write('cd "$dir" && \\\n') f.write('%s \\\n' % firstcompiler) f.write(' "$@" \\\n') f.write(' -c "$base.$ext"\n') os.chmod('build/%s/scripts/compiledefault' % host,0o755) # ----- libcpucycles os.makedirs('build/%s/cpucycles' % host,exist_ok=True) os.makedirs('build/%s/package/man/man1' % host,exist_ok=True) os.makedirs('build/%s/package/man/man3' % host,exist_ok=True) dirlinksym('build/%s/cpucycles'%host,'cpucycles.h','../src/cpucycles/cpucycles.h') dirlinksym('build/%s/cpucycles'%host,'cpucycles_internal.h','../src/cpucycles/cpucycles_internal.h') shutil.copy2('cpucycles/cpucycles.h','build/%s/package/include/cpucycles.h'%host) shutil.copy2('doc/man/cpucycles-info.1','build/%s/package/man/man1/cpucycles-info.1'%host) shutil.copy2('doc/man/cpucycles.3','build/%s/package/man/man3/cpucycles.3'%host) with open('build/%s/cpucycles/compile-ticks' % host,'w') as f: f.write('#!/bin/sh\n') f.write('arch="$1"; shift\n') f.write('x="$1"; shift\n') f.write('for source in try-"$arch"-"$x".c try-default-zero.c\n') f.write('do\n') f.write(' cp "$source" "$arch"-"$x".c\n') f.write(' %s \\\n' % firstcompiler) f.write(' -Dticks=cpucycles_ticks_"$arch"_"$x" \\\n') f.write(' -Dticks_setup=cpucycles_ticks_"$arch"_"$x"_setup \\\n') f.write(' -c "$arch"-"$x".c\n') f.write(' case $? in\n') f.write(' 0) break ;;\n') f.write(' 111) exit 111 ;;\n') f.write(' *) echo "skipping option that did not compile" ;;\n') f.write(' esac\n') f.write('done\n') os.chmod('build/%s/cpucycles/compile-ticks' % host,0o755) cpucyclesoptions = [] cpucyclesofiles = [] with open('cpucycles/options') as f: for line in f: line = line.strip() if line == '': continue if line[0] == '#': continue base = line.split()[0] if not os.path.exists('cpucycles/%s.c' % base): continue cpucycles = base.split('-') if len(cpucycles) != 2: continue if cpucycles[0] not in (host,'default'): continue cpucyclesoptions += [cpucycles] cpucyclesoptions += [['default','zero']] # must be last for cpucycles in cpucyclesoptions: base = '-'.join(cpucycles) cpucyclesofiles += ['cpucycles/%s.o' % base] dirlinksym('build/%s/cpucycles'%host,'try-%s.c'%base,'../src/cpucycles/%s.c'%base) M = 'cpucycles/%s.o: cpucycles/try-%s.c cpucycles/try-default-zero.c\n' % (base,base) M += '\tcd cpucycles && ./compile-ticks %s %s\n' % tuple(cpucycles) M += '\n' makefile = M + makefile for fn in sorted(os.listdir('cpucycles')): if not fn.endswith('.c'): continue if '-' in fn: continue base = fn[:-2] cpucyclesofiles += ['cpucycles/%s.o' % base] dirlinksym('build/%s/cpucycles'%host,fn,'../src/cpucycles/%s'%fn) M = 'cpucycles/%s.o: cpucycles/%s.c\n' % (base,base) M += '\tscripts/compiledefault cpucycles %s c\n' % base M += '\n' makefile = M + makefile with open('build/%s/cpucycles/options.inc' % host,'w') as f: f.write('#define NUMOPTIONS %d\n' % len(cpucyclesoptions)) f.write('#define DEFAULTOPTION (NUMOPTIONS-1)\n') f.write('\n') for cpucycles in cpucyclesoptions: f.write('extern long long cpucycles_ticks_%s_%s_setup(void);\n' % (cpucycles[0],cpucycles[1])) f.write('extern long long cpucycles_ticks_%s_%s(void);\n' % (cpucycles[0],cpucycles[1])) f.write('\n') f.write('static struct {\n') f.write(' const char *implementation;\n') f.write(' long long (*ticks_setup)(void);\n') f.write(' long long (*ticks)(void);\n') f.write('} options[NUMOPTIONS] = {\n') for cpucycles in cpucyclesoptions: f.write('{ "%s-%s", cpucycles_ticks_%s_%s_setup, cpucycles_ticks_%s_%s },\n' % (cpucycles[0],cpucycles[1],cpucycles[0],cpucycles[1],cpucycles[0],cpucycles[1])) f.write('} ;\n') dirlinksym('build/%s/scripts'%host,'staticlib','../src/scripts-build/staticlib') M = 'package/lib/libcpucycles.a: scripts/staticlib %s\n' % ' '.join(cpucyclesofiles) M += '\tscripts/staticlib %s\n' % ' '.join(cpucyclesofiles) M += '\n' makefile = M + makefile with open('build/%s/scripts/sharedlib' % host,'w') as f: f.write('#!/bin/sh\n') f.write('\n') f.write('%s -shared \\\n' % firstcompiler) if rpath: f.write(' -Wl,-rpath=%s \\\n' % rpath) f.write(f' -Wl,-{soname},libcpucycles.{so1} \\\n') f.write(f' -o package/lib/libcpucycles.{so1} \\\n') f.write(' "$@"\n') f.write(f'chmod 644 package/lib/libcpucycles.{so1}\n') os.chmod('build/%s/scripts/sharedlib' % host,0o755) M = f'package/lib/libcpucycles.{so1}: scripts/sharedlib %s\n' % ' '.join(cpucyclesofiles) M += '\tscripts/sharedlib %s\n' % ' '.join(cpucyclesofiles) M += '\n' makefile = M + makefile M = f'package/lib/libcpucycles.{so}: package/lib/libcpucycles.{so1}\n' M += f'\trm -f package/lib/libcpucycles.{so}\n' M += f'\tln -s libcpucycles.{so1} package/lib/libcpucycles.{so}\n' M += '\n' makefile = M + makefile # ----- command os.makedirs('build/%s/command'%host) for c in sorted(os.listdir('command')): dirlinksym('build/%s/command'%host,c,'../src/command/%s'%c) dirlinksym('build/%s/command'%host,'bin','../package/bin') dirlinksym('build/%s/command'%host,'lib','../package/lib') dirlinksym('build/%s/command'%host,'include','../package/include') with open('build/%s/command/link' % host,'w') as f: f.write('#!/bin/sh\n') f.write('target="$1"; shift\n') f.write('%s \\\n' % firstcompiler) f.write(' -o "$target" "$@"\n') os.chmod('build/%s/command/link' % host,0o755) commands = [] for fn in sorted(os.listdir('command')): if not fn.endswith('.c'): continue libs = ['libcpucycles'] base = fn[:-2] M = 'command/%s.o: command/%s.c\n' % (base,base) M += '\tscripts/compiledefault command %s c -I include\n' % base M += '\n' makefile = M + makefile M = 'package/bin/%s: command/%s.o%s\n' % (base,base,''.join(' package/lib/%s.%s' % (x,so) for x in libs)) M += f'\tcd command && ./link bin/%s %s.o%s {syslibs}\n' % (base,base,''.join(' lib/%s.%s' % (x,so) for x in libs)) M += '\n' makefile = M + makefile commands += ['package/bin/%s' % base] M = 'commands: %s\n' % ' '.join(commands) M += '\n' makefile = M + makefile # ----- make install M = 'install: scripts/install default\n' M += '\tscripts/install %s\n' % prefix M += '\n' makefile = M + makefile # ----- make default M = f'default: package/lib/libcpucycles.a package/lib/libcpucycles.{so} package/lib/libcpucycles.{so1} \\\n' M += 'commands\n' M += '\n' makefile = M + makefile with open('build/%s/Makefile' % host,'w') as f: f.write(makefile) # ----- build/0, build/Makefile dirlinksym('build','0',host) with open('build/Makefile','w') as f: f.write('default:\n') f.write('\tcd %s && $(MAKE)\n' % host) f.write('\n') f.write('install:\n') f.write('\tcd %s && $(MAKE) install\n' % host) f.write('\n') f.write('clean:\n') f.write('\trm -r %s\n' % host)