diff --git a/ChangeLog b/ChangeLog index 025edad..959c375 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +Mon Sep 15 00:43:29 2008 Minero Aoki + + * net/loveruby/cflat/compiler/Compiler.java: handle multiple + source files. + + * net/loveruby/cflat/compiler/Options.java: new class forked from + Compiler.java. Command option parser. + + * net/loveruby/cflat/compiler/SourceFile.java: new class. + + * net/loveruby/cflat/compiler/LdOption.java: new class. + + * net/loveruby/cflat/compiler/LdArg.java: new interface. + + * net/loveruby/cflat/exception/OptionParseError.java: new error + class. + + * test: test multiple input. + Sun Sep 14 20:08:19 2008 Minero Aoki * net/loveruby/cflat/compiler/Compiler.java: use CflatToken to diff --git a/net/loveruby/cflat/compiler/Compiler.java b/net/loveruby/cflat/compiler/Compiler.java index c3e9cb2..785c2e7 100644 --- a/net/loveruby/cflat/compiler/Compiler.java +++ b/net/loveruby/cflat/compiler/Compiler.java @@ -8,10 +8,11 @@ import java.util.*; import java.io.*; public class Compiler { - static final private String programId = "cbc"; + static final public String ProgramID = "cbc"; + static final public String Version = "1.0.0"; static public void main(String[] args) { - new Compiler(programId).commandMain(args); + new Compiler(ProgramID).commandMain(args); } protected ErrorHandler errorHandler; @@ -25,19 +26,41 @@ public class Compiler { } public void commandMain(String[] origArgs) { - Options opts = parseOptions(listFromArray(origArgs)); + Options opts = new Options(defaultTypeTable(), new LibraryLoader()); + List srcs = null; + try { + srcs = opts.parse(Arrays.asList(origArgs)); + } + catch (OptionParseError err) { + errorHandler.error(err.getMessage()); + errorHandler.error("Try cbc --help for option usage"); + System.exit(1); + } if (opts.isMode("--check-syntax")) { - if (isValidSyntax(opts)) { - System.out.println("Syntax OK"); - System.exit(0); - } - else { - System.exit(1); + Iterator inputs = srcs.iterator(); + boolean failed = false; + while (inputs.hasNext()) { + SourceFile src = (SourceFile)inputs.next(); + if (isValidSyntax(src, opts)) { + System.out.println(src.name() + ": Syntax OK"); + } + else { + System.out.println(src.name() + ": Syntax Error"); + failed = true; + } } + System.exit(failed ? 1 : 0); } else { try { - compileFile(opts); + Iterator inputs = srcs.iterator(); + while (inputs.hasNext()) { + SourceFile src = (SourceFile)inputs.next(); + compileFile(src, opts); + } + if (! opts.isLinkRequired()) System.exit(0); + generateExecutable(opts); + System.exit(0); } catch (CompileException ex) { errorHandler.error(ex.getMessage()); @@ -46,148 +69,14 @@ public class Compiler { } } - class Options { - public String mode; - public String inputFile; - public String outputFile; - public boolean verbose; - public boolean debugParser; - //public boolean debugBuild; - public TypeTable typeTable; - public LibraryLoader loader; - //public List asOpts; // List - //public List ldOpts; // List - //public List ldArgs; // List - - public boolean isMode(String m) { - return mode == null ? false : mode.equals(m); - } - } - - protected Options parseOptions(List args) { - ListIterator it = args.listIterator(); - Options opts = new Options(); - opts.typeTable = defaultTypeTable(); - opts.loader = new LibraryLoader(); - while (it.hasNext()) { - String arg = (String)it.next(); - if (arg.equals("-")) { - System.err.println("FIXME: stdin is not supported yet"); - System.exit(1); - } - else if (arg.equals("--")) { - // "--" Stops command line processing - it.remove(); - break; - } - else if (arg.startsWith("-")) { - if (arg.equals("--check-syntax") - || arg.equals("--dump-tokens") - || arg.equals("--dump-ast") - || arg.equals("--dump-stmt") - || arg.equals("--dump-reference") - || arg.equals("--dump-semantic") - || arg.equals("-S") - || arg.equals("--dump-asm") - || arg.equals("-c")) { - if (opts.mode != null) { - errorExit(opts.mode + " option and " - + arg + " option is exclusive"); - } - opts.mode = arg; - } - else if (arg.startsWith("-I")) { - opts.loader.addLoadPath(getOptArg(arg, it)); - } - else if (arg.equals("--debug-parser")) { - opts.debugParser = true; - } - else if (arg.startsWith("-o")) { - opts.outputFile = getOptArg(arg, it); - } - // FIXME: compile options - //else if (arg.equals("-fPIC")) - //else if (arg.startsWith("-O")) - // FIXME: assemble options - //else if (arg.startsWith("-Wa,")) - //else if (arg.equals("-Xassembler")) - // FIXME: link options - //else if (arg.equals("-static")) - //else if (arg.equals("-shared")) - //else if (arg.startsWith("-L")) - //else if (arg.startsWith("-l")) - //else if (arg.startsWith("-Wl,")) - //else if (arg.equals("-Xlinker")) - else if (arg.equals("-v")) { - opts.verbose = true; - } - else if (arg.equals("--help")) { - printUsage(System.out); - System.exit(0); - } - else { - System.err.println("unknown option: " + arg); - printUsage(System.err); - System.exit(1); - } - it.remove(); - } - } - - // FIXME: handle many input files - if (args.size() == 0) errorExit("no input file"); - if (args.size() > 1) errorExit("too many input files"); - opts.inputFile = (String)args.get(0); - - return opts; - } - - protected void printUsage(PrintStream out) { - // --dump-reference is hidden option - out.println("Usage: cbc [option] file"); - out.println(" --check-syntax Checks syntax."); - out.println(" --debug-parser Dumps parsing process."); - out.println(" --dump-tokens Parses source file and dumps tokens."); - out.println(" --dump-ast Parses source file and dumps AST."); - out.println(" --dump-semantic Checks semantics and dumps AST."); - out.println(" -S Generates an assembly source."); - out.println(" --dump-asm Dumps an assembly source."); - out.println(" -c Generates an object file."); - out.println(" -I PATH Adds import file loading path."); - out.println(" -o PATH Places output in file PATH."); - out.println(" --help Prints this message and quit."); - } - - private List listFromArray(Object[] a) { - List list = new ArrayList(); - for (int i = 0; i < a.length; i++) { - list.add(a[i]); - } - return list; - } - - private String getOptArg(String arg, ListIterator it) { - String path = arg.substring(2); - if (path.length() != 0) { // -Ipath - return path; - } - else { // -I path - if (! it.hasNext()) { - errorExit("-I option missing argument"); - } - it.remove(); - return (String)it.next(); - } - } - private void errorExit(String msg) { errorHandler.error(msg); System.exit(1); } - protected boolean isValidSyntax(Options opts) { + protected boolean isValidSyntax(SourceFile src, Options opts) { try { - parseFile(opts); + parseFile(src, opts); return true; } catch (SyntaxException ex) { @@ -199,39 +88,44 @@ public class Compiler { } } - protected void compileFile(Options opts) throws CompileException { - AST ast = parseFile(opts); - if (opts.isMode("--dump-tokens")) { - dumpTokens(ast.sourceTokens(), System.out); - return; + protected void compileFile(SourceFile src, Options opts) + throws CompileException { + if (src.isCflatSource()) { + AST ast = parseFile(src, opts); + if (opts.isMode("--dump-tokens")) { + dumpTokens(ast.sourceTokens(), System.out); + return; + } + if (opts.isMode("--dump-ast")) { + ast.dump(); + return; + } + if (opts.isMode("--dump-stmt")) { + findStmt(ast).dump(); + return; + } + semanticAnalysis(ast, opts); + if (opts.isMode("--dump-reference")) return; + if (opts.isMode("--dump-semantic")) { + ast.dump(); + return; + } + String asm = generateAssembly(ast, opts); + if (opts.isMode("--dump-asm")) { + System.out.println(asm); + return; + } + writeFile(src.asmFileName(opts), asm); + src.setCurrentName(src.asmFileName(opts)); + if (opts.isMode("-S")) { + return; + } } - if (opts.isMode("--dump-ast")) { - ast.dump(); - return; + if (! opts.isAssembleRequired()) return; + if (src.isAssemblySource()) { + assemble(src.asmFileName(opts), src.objFileName(opts), opts); + src.setCurrentName(src.objFileName(opts)); } - if (opts.isMode("--dump-stmt")) { - findStmt(ast).dump(); - return; - } - semanticAnalysis(ast, opts); - if (opts.isMode("--dump-semantic")) { - ast.dump(); - return; - } - String asm = CodeGenerator.generate(ast, opts.typeTable, errorHandler); - if (opts.isMode("--dump-asm")) { - System.out.println(asm); - return; - } - writeFile(asmFileName(opts), asm); - if (opts.isMode("-S")) { - return; - } - assemble(asmFileName(opts), objFileName(opts), opts); - if (opts.isMode("-c")) { - return; - } - link(objFileName(opts), exeFileName(opts), opts); } protected void dumpTokens(Iterator tokens, PrintStream s) { @@ -267,61 +161,66 @@ public class Compiler { return null; // never reach } - protected AST parseFile(Options opts) + protected AST parseFile(SourceFile src, Options opts) throws SyntaxException, FileException { - return Parser.parseFile(new File(opts.inputFile), - opts.loader, + return Parser.parseFile(new File(src.currentName()), + opts.loader(), errorHandler, - opts.debugParser); + opts.doesDebugParser()); } protected void semanticAnalysis(AST ast, Options opts) throws SemanticException { JumpResolver.resolve(ast, errorHandler); LocalReferenceResolver.resolve(ast, errorHandler); - TypeResolver.resolve(ast, opts.typeTable, errorHandler); - opts.typeTable.semanticCheck(errorHandler); + TypeResolver.resolve(ast, opts.typeTable(), errorHandler); + opts.typeTable().semanticCheck(errorHandler); DereferenceChecker.check(ast, errorHandler); if (opts.isMode("--dump-reference")) { ast.dump(); - System.exit(1); + return; } - new TypeChecker(opts.typeTable, errorHandler).check(ast); + new TypeChecker(opts.typeTable(), errorHandler).check(ast); + } + + protected String generateAssembly(AST ast, Options opts) { + return CodeGenerator.generate(ast, opts.typeTable(), errorHandler); } protected void assemble(String srcPath, String destPath, Options opts) throws IPCException { - String[] cmd = { - "as", - "-o", destPath, - srcPath - }; - invoke(cmd, opts.verbose); + List cmd = new ArrayList(); + cmd.add("as"); + cmd.addAll(opts.asOptions()); + cmd.add("-o"); + cmd.add(destPath); + cmd.add(srcPath); + invoke(cmd, opts.isVerboseMode()); } - protected void link(String srcPath, - String destPath, - Options opts) throws IPCException { - String[] cmd = { - "ld", - "-dynamic-linker", "/lib/ld-linux.so.2", - "/usr/lib/crt1.o", - "/usr/lib/crti.o", - srcPath, - "/usr/lib/libc_nonshared.a", - "-lc", - "/usr/lib/crtn.o", - "-o", destPath - }; - invoke(cmd, opts.verbose); + protected void generateExecutable(Options opts) throws IPCException { + List cmd = new ArrayList(); + cmd.add("ld"); + // FIXME: -dynamic-linker required only on dynamic linking + cmd.add("-dynamic-linker"); + cmd.add("/lib/ld-linux.so.2"); + if (! opts.noStartFiles()) cmd.add("/usr/lib/crt1.o"); + if (! opts.noStartFiles()) cmd.add("/usr/lib/crti.o"); + cmd.addAll(opts.ldArgs()); + if (! opts.noDefaultLibs()) cmd.add("-lc"); + if (! opts.noStartFiles()) cmd.add("/usr/lib/crtn.o"); + cmd.add("-o"); + cmd.add(opts.exeFileName()); + invoke(cmd, opts.isVerboseMode()); } - public void invoke(String[] cmd, boolean debug) throws IPCException { + protected void invoke(List cmdArgs, boolean debug) throws IPCException { if (debug) { - dumpCommand(cmd); + dumpCommand(cmdArgs.iterator()); } try { + String[] cmd = stringListToArray(cmdArgs); Process proc = Runtime.getRuntime().exec(cmd); proc.waitFor(); passThrough(proc.getInputStream()); @@ -342,11 +241,23 @@ public class Compiler { } } - protected void dumpCommand(String[] cmd) { + protected String[] stringListToArray(List list) { + String[] a = new String[list.size()]; + int idx = 0; + Iterator it = list.iterator(); + while (it.hasNext()) { + Object o = it.next(); + a[idx++] = o.toString(); + } + return a; + } + + protected void dumpCommand(Iterator args) { String sep = ""; - for (int i = 0; i < cmd.length; i++) { + while (args.hasNext()) { + String arg = args.next().toString(); System.out.print(sep); sep = " "; - System.out.print(cmd[i]); + System.out.print(arg); } System.out.println(""); } @@ -380,35 +291,4 @@ public class Compiler { throw new FileException("file error"); } } - - protected String asmFileName(Options opts) { - return opts.isMode("-S") && opts.outputFile != null - ? opts.outputFile - : baseName(opts.inputFile, true) + ".s"; - } - - protected String objFileName(Options opts) { - return opts.isMode("-c") && opts.outputFile != null - ? opts.outputFile - : baseName(opts.inputFile, true) + ".o"; - } - - protected String exeFileName(Options opts) { - return opts.outputFile != null - ? opts.outputFile - : baseName(opts.inputFile, true); - } - - protected String baseName(String path) { - return new File(path).getName(); - } - - protected String baseName(String path, boolean stripExt) { - if (stripExt) { - return new File(path).getName().replaceFirst("\\.[^.]*$", ""); - } - else { - return baseName(path); - } - } } diff --git a/net/loveruby/cflat/compiler/LdArg.java b/net/loveruby/cflat/compiler/LdArg.java new file mode 100644 index 0000000..42a2a87 --- /dev/null +++ b/net/loveruby/cflat/compiler/LdArg.java @@ -0,0 +1,7 @@ +package net.loveruby.cflat.compiler; + +// package private +interface LdArg { + abstract public String toString(); + abstract public boolean isSourceFile(); +} diff --git a/net/loveruby/cflat/compiler/LdOption.java b/net/loveruby/cflat/compiler/LdOption.java new file mode 100644 index 0000000..84e00be --- /dev/null +++ b/net/loveruby/cflat/compiler/LdOption.java @@ -0,0 +1,18 @@ +package net.loveruby.cflat.compiler; + +// package private +class LdOption implements LdArg { + protected String option; + + public LdOption(String option) { + this.option = option; + } + + public boolean isSourceFile() { + return false; + } + + public String toString() { + return this.option; + } +} diff --git a/net/loveruby/cflat/compiler/Options.java b/net/loveruby/cflat/compiler/Options.java new file mode 100644 index 0000000..2e2d967 --- /dev/null +++ b/net/loveruby/cflat/compiler/Options.java @@ -0,0 +1,345 @@ +package net.loveruby.cflat.compiler; +import net.loveruby.cflat.type.TypeTable; +import net.loveruby.cflat.exception.*; +import java.util.*; +import java.io.*; + +// package scope +class Options { + protected String mode; + protected TypeTable typeTable; + protected LibraryLoader loader; + protected String outputFileName; + protected boolean verbose; + protected boolean debugParser; + protected List asOptions; // List + protected List ldArgs; // List + protected boolean noStartFiles = false; + protected boolean noDefaultLibs = false; + + public Options(TypeTable typeTable, LibraryLoader loader) { + this.typeTable = typeTable; + this.loader = loader; + this.asOptions = new ArrayList(); + this.ldArgs = new ArrayList(); + } + + public boolean isMode(String m) { + return mode.equals(m); + } + + public boolean isAssembleRequired() { + return modeLevel(mode) >= MODE_LEVEL_ASSEMBLE; + } + + public boolean isLinkRequired() { + return modeLevel(mode) >= MODE_LEVEL_LINK; + } + + static final protected String defaultMode = "link"; + static protected List modeLevels; + static final protected int MODE_LEVEL_ASSEMBLE; + static final protected int MODE_LEVEL_LINK; + + static { + modeLevels = new ArrayList(); + modeLevels.add("--check-syntax"); + modeLevels.add("--dump-tokens"); + modeLevels.add("--dump-ast"); + modeLevels.add("--dump-semantic"); + modeLevels.add("--dump-reference"); + modeLevels.add("--dump-asm"); + modeLevels.add("-S"); + modeLevels.add("-c"); // assemble required + modeLevels.add(defaultMode); // link required + + MODE_LEVEL_ASSEMBLE = modeLevels.indexOf("-c"); + MODE_LEVEL_LINK = modeLevels.indexOf(defaultMode); + }; + + protected int modeLevel(String mode) { + int idx = modeLevels.indexOf(mode); + if (idx < 0) throw new Error("unknown compiler mode: " + mode); + return idx; + } + + public String outputFileNameFor(String mode) { + return isMode(mode) ? outputFileName : null; + } + + public String exeFileName() { + if (outputFileName != null) return outputFileName; + List srcs = sourceFiles(); + if (srcs.size() == 1) { + SourceFile src = (SourceFile)srcs.get(0); + return src.exeFileName(this); + } + else { + return "a.out"; + } + } + + protected List sourceFiles() { + Iterator args = ldArgs.iterator(); + List result = new ArrayList(); + while (args.hasNext()) { + LdArg arg = (LdArg)args.next(); + if (arg.isSourceFile()) { + result.add(arg); + } + } + return result; + } + + public TypeTable typeTable() { + return this.typeTable; + } + + public LibraryLoader loader() { + return this.loader; + } + + public String outputFileName() { + return this.outputFileName; + } + + public boolean isVerboseMode() { + return this.verbose; + } + + public boolean doesDebugParser() { + return this.debugParser; + } + + // List + public List asOptions() { + return this.asOptions; + } + + // List + public List ldArgs() { + return this.ldArgs; + } + + public boolean noStartFiles() { + return this.noStartFiles; + } + + public boolean noDefaultLibs() { + return this.noDefaultLibs; + } + + /** Returns List. */ + public List parse(List argsList) { + ListIterator args = argsList.listIterator(); + List srcs = new ArrayList(); + while (args.hasNext()) { + String arg = (String)args.next(); + // FIXME: stdin + if (arg.equals("-")) { + System.err.println("FIXME: stdin is not supported yet"); + System.exit(1); + } + else if (arg.equals("--")) { + // "--" Stops command line processing + break; + } + else if (arg.startsWith("-")) { + if (arg.equals("--check-syntax") + || arg.equals("--dump-tokens") + || arg.equals("--dump-ast") + || arg.equals("--dump-stmt") + || arg.equals("--dump-reference") + || arg.equals("--dump-semantic") + || arg.equals("-S") + || arg.equals("--dump-asm") + || arg.equals("-c")) { + if (mode != null) { + parseError(mode + " option and " + + arg + " option is exclusive"); + } + mode = arg; + } + else if (arg.startsWith("-I")) { + loader.addLoadPath(getOptArg(arg, args)); + } + else if (arg.equals("--debug-parser")) { + debugParser = true; + } + else if (arg.startsWith("-o")) { + outputFileName = getOptArg(arg, args); + } + // FIXME: PIC, PIE + //else if (arg.equals("-fpie")) + //else if (arg.equals("-fPIC")) + //else if (arg.equals("-fpie")) + //else if (arg.equals("-fPIE")) + // FIXME: optimization + //else if (arg.startsWith("-O")) + else if (arg.startsWith("-Wa,")) { + asOptions.addAll(parseCommaSeparatedOptions(arg)); + } + else if (arg.equals("-Xassembler")) { + asOptions.add(nextArg(arg, args)); + } + else if (arg.equals("-static")) { + ldArgs.add(new LdOption(arg)); + } + else if (arg.equals("-shared")) { + ldArgs.add(new LdOption(arg)); + } + else if (arg.startsWith("-L")) { + ldArgs.add(new LdOption("-L" + getOptArg(arg, args))); + } + else if (arg.startsWith("-l")) { + ldArgs.add(new LdOption("-l" + getOptArg(arg, args))); + } + else if (arg.equals("-nostartfiles")) { + noStartFiles = true; + } + else if (arg.equals("-nodefaultlibs")) { + noDefaultLibs = true; + } + else if (arg.equals("-nostdlib")) { + noStartFiles = true; + noDefaultLibs = true; + } + else if (arg.startsWith("-Wl,")) { + Iterator opts = parseCommaSeparatedOptions(arg).iterator(); + while (opts.hasNext()) { + String opt = (String)opts.next(); + ldArgs.add(new LdOption(opt)); + } + } + else if (arg.equals("-Xlinker")) { + ldArgs.add(new LdOption(nextArg(arg, args))); + } + // FIXME: -pie + //else if (arg.equals("-pie")) + else if (arg.equals("-v")) { + verbose = true; + } + else if (arg.equals("--version")) { + System.out.println(Compiler.ProgramID + + " version " + Compiler.Version); + System.exit(0); + } + else if (arg.equals("--help")) { + printUsage(System.out); + System.exit(0); + } + else { + parseError("unknown option: " + arg); + } + } + else { + // source file + addSourceFile(srcs, ldArgs, arg); + } + } + // args has more arguments when "--" is appeared. + while (args.hasNext()) { + String arg = (String)args.next(); + addSourceFile(srcs, ldArgs, arg); + } + if (srcs.isEmpty()) parseError("no input file"); + if (mode == null) { + mode = defaultMode; + } + if (! isLinkRequired() && outputFileName != null && srcs.size() > 1) { + parseError("-o option requires only 1 input not on linking"); + } + return srcs; + } + + protected void parseError(String msg) { + throw new OptionParseError(msg); + } + + protected void addSourceFile(List srcs, List ldArgs, String sourceName) { + SourceFile src = new SourceFile(sourceName); + srcs.add(src); + // Original argument order does matter when linking. + ldArgs.add(src); + } + + protected String getOptArg(String opt, ListIterator args) { + String path = opt.substring(2); + if (path.length() != 0) { // -Ipath + return path; + } + else { // -I path + return nextArg(opt, args); + } + } + + protected String nextArg(String opt, ListIterator args) { + if (! args.hasNext()) { + parseError("missing argument for " + opt); + } + return (String)args.next(); + } + + /** "-Wl,-rpath,/usr/local/lib" -> ["-rpath", "/usr/local/lib"] */ + protected List parseCommaSeparatedOptions(String opt) { + List opts = arrayToList(opt.split(",")); + opts.remove(0); // remove "-Wl" etc. + if (opts.isEmpty()) { + parseError("missing argument for " + opt); + } + return opts; + } + + protected List arrayToList(Object[] ary) { + List result = new ArrayList(); + for (int i = 0; i < ary.length; i++) { + result.add(ary[i]); + } + return result; + } + + public void printUsage(PrintStream out) { + out.println("Usage: cbc [options] file..."); + out.println("Generic Options:"); + out.println(" --check-syntax Checks syntax."); + out.println(" --dump-tokens Parses source file and dumps tokens."); + out.println(" --dump-ast Parses source file and dumps AST."); + out.println(" --dump-semantic Checks semantics and dumps AST."); + // --dump-reference is hidden option + out.println(" -S Generates an assembly source."); + out.println(" --dump-asm Dumps an assembly source."); + out.println(" -c Generates an object file."); + out.println(" -o PATH Places output in file PATH."); + out.println(" -v Verbose mode."); + out.println(" --version Shows compiler version."); + out.println(" --help Prints this message and quit."); + out.println(""); + out.println("Parser Options:"); + out.println(" --debug-parser Dumps parsing process."); + out.println(""); + out.println("Compiler Options:"); + out.println(" -I PATH Adds PATH as import file directory."); + //out.println(" -O Enables optimization."); + //out.println(" -O1, -O2, -O3 Equivalent to -O."); + //out.println(" -Os Equivalent to -O."); + //out.println(" -O0 Disables optimization."); + //out.println(" -fPIC Generates PIC assembly."); + //out.println(" -fPIE Generates PIE assembly."); + out.println(""); + out.println("Assembler Options:"); + out.println(" -Wa,OPT Passes OPT to the assembler (as)."); + out.println(" -Xassembler OPT Passes OPT to the assembler (as)."); + out.println(""); + out.println("Linker Options:"); + out.println(" -l LIB Links the library LIB."); + out.println(" -L PATH Adds PATH as library directory."); + //out.println(" -shared Linkes with shared library (default)."); + //out.println(" -static Linkes with static library."); + //out.println(" -pie Generates PIE."); + out.println(" -nostartfiles Do not link startup files."); + out.println(" -nodefaultlibs Do not link default libraries."); + out.println(" -nostdlib Enables -nostartfiles and -nodefaultlibs."); + out.println(" -Wl,OPT Passes OPT to the linker (ld)."); + out.println(" -Xlinker OPT Passes OPT to the linker (ld)."); + } +} diff --git a/net/loveruby/cflat/compiler/SourceFile.java b/net/loveruby/cflat/compiler/SourceFile.java new file mode 100644 index 0000000..37a1a32 --- /dev/null +++ b/net/loveruby/cflat/compiler/SourceFile.java @@ -0,0 +1,100 @@ +package net.loveruby.cflat.compiler; +import java.io.File; + +// package private +class SourceFile implements LdArg { + protected String originalName; + protected String currentName; + + public SourceFile(String name) { + this.originalName = name; + this.currentName = name; + } + + public String name() { + return originalName(); + } + + public String originalName() { + return this.originalName; + } + + public String currentName() { + return this.currentName; + } + + public void setCurrentName(String name) { + this.currentName = name; + } + + public boolean isCflatSource() { + return extName(currentName).equals(".cb"); + } + + public boolean isAssemblySource() { + return extName(currentName).equals(".s"); + } + + public boolean isObjectFile() { + return extName(currentName).equals(".o"); + } + + public boolean isSharedLibrary() { + return extName(currentName).equals(".so"); + } + + public boolean isStaticLibrary() { + return extName(currentName).equals(".a"); + } + + public boolean isExecutable() { + return extName(currentName).equals(""); + } + + public String asmFileName(Options opts) { + return or(opts.outputFileNameFor("-S"), replaceExt(".s")); + } + + public String objFileName(Options opts) { + return or(opts.outputFileNameFor("-c"), replaceExt(".o")); + } + + public String exeFileName(Options opts) { + return or(opts.outputFileName, replaceExt("")); + } + + protected String or(String x, String y) { + return x != null ? x : y; + } + + protected String replaceExt(String ext) { + return baseName(originalName, true) + ext; + } + + protected String baseName(String path) { + return new File(path).getName(); + } + + protected String baseName(String path, boolean stripExt) { + if (stripExt) { + return new File(path).getName().replaceFirst("\\.[^.]*$", ""); + } + else { + return baseName(path); + } + } + + protected String extName(String path) { + int idx = path.lastIndexOf("."); + if (idx < 0) return ""; + return path.substring(idx); + } + + public boolean isSourceFile() { + return true; + } + + public String toString() { + return currentName; + } +} diff --git a/net/loveruby/cflat/exception/OptionParseError.java b/net/loveruby/cflat/exception/OptionParseError.java new file mode 100644 index 0000000..ac99aa9 --- /dev/null +++ b/net/loveruby/cflat/exception/OptionParseError.java @@ -0,0 +1,7 @@ +package net.loveruby.cflat.exception; + +public class OptionParseError extends Error { + public OptionParseError(String msg) { + super(msg); + } +} diff --git a/test/shunit.sh b/test/shunit.sh index 35e9d12..b83ebd5 100644 --- a/test/shunit.sh +++ b/test/shunit.sh @@ -180,6 +180,20 @@ assert_not_coredump() { return 0 } +assert_file() { + local path="$1"; shift + + shunit_begin_test + if [ ! -f "$path" ] + then + echo "not exist or not a file: $file" + echo "----" + shunit_test_failed + return 1 + fi + return 0 +} + assert_not_exist() { local file="$1"; shift diff --git a/test/src1.cb b/test/src1.cb new file mode 100644 index 0000000..7e650ec --- /dev/null +++ b/test/src1.cb @@ -0,0 +1 @@ +int f(int x) { return x * x; } diff --git a/test/src1.hb b/test/src1.hb new file mode 100644 index 0000000..408c9f7 --- /dev/null +++ b/test/src1.hb @@ -0,0 +1 @@ +extern int f(int x); diff --git a/test/src2.cb b/test/src2.cb new file mode 100644 index 0000000..e030076 --- /dev/null +++ b/test/src2.cb @@ -0,0 +1,2 @@ +import src1; +int main(int argc, char **argv) { return f(2); } diff --git a/test/test_cbc.sh b/test/test_cbc.sh index d53a844..00020da 100644 --- a/test/test_cbc.sh +++ b/test/test_cbc.sh @@ -289,6 +289,11 @@ test_32_noreturn() { assert_stat 0 ./noreturn } +test_33_multipleinput() { + assert_compile_success src1.cb src2.cb -o src + assert_status 4 ./src +} + ### ### Local Assertions ###