mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-04-03 23:31:52 +00:00
Implemented OutputFileManager which ensures that the output directory, basename and extension follows well defined rules. Closes #663
This commit is contained in:
parent
f67c2712d3
commit
867a816eae
@ -156,65 +156,42 @@ public class Compiler {
|
||||
if(cFiles.size() == 0)
|
||||
throw new CompileError("Error! You must supply at least one file to compile!");
|
||||
|
||||
final Path primaryCFile = cFiles.get(0);
|
||||
String primaryCFileBaseName = removeFileNameExtension(primaryCFile.toString());
|
||||
program.setPrimaryFileName(primaryCFileBaseName);
|
||||
Path currentPath = new File(".").toPath();
|
||||
CParser cParser = initializeParser(defines, cFiles, currentPath);
|
||||
|
||||
try {
|
||||
Path currentPath = new File(".").toPath();
|
||||
CParser cParser = initializeParser(defines, cFiles, currentPath);
|
||||
// Parse the files
|
||||
KickCParser.FileContext cFileContext = cParser.getParser().file();
|
||||
|
||||
// Parse the files
|
||||
KickCParser.FileContext cFileContext = cParser.getParser().file();
|
||||
|
||||
if(callingConvention == null) {
|
||||
callingConvention = Procedure.CallingConvention.PHI_CALL;
|
||||
}
|
||||
|
||||
// Find encoding
|
||||
StringEncoding encoding = program.getTargetPlatform().getEncoding();
|
||||
if(encoding==null)
|
||||
encoding = StringEncoding.SCREENCODE_MIXED;
|
||||
|
||||
// Find default interrupt type
|
||||
String interruptType = program.getTargetPlatform().getInterruptType();
|
||||
|
||||
Pass0GenerateStatementSequence pass0GenerateStatementSequence = new Pass0GenerateStatementSequence(cParser, cFileContext, program, callingConvention, encoding, interruptType);
|
||||
pass0GenerateStatementSequence.generate();
|
||||
|
||||
pass1GenerateSSA();
|
||||
pass2Optimize();
|
||||
pass2UnrollLoops();
|
||||
pass2InlineConstants();
|
||||
pass2FinalizeAllNumbers();
|
||||
|
||||
//getLog().append("\nCONTROL FLOW GRAPH PASS 2");
|
||||
//getLog().append(program.getGraph().toString(program));
|
||||
|
||||
//getLog().append("SYMBOL TABLE PASS 2");
|
||||
//getLog().append(program.getScope().toString(program, null));
|
||||
|
||||
pass3Analysis();
|
||||
pass4RegisterAllocation();
|
||||
pass5GenerateAndOptimizeAsm();
|
||||
} catch(Exception e) {
|
||||
throw e;
|
||||
if(callingConvention == null) {
|
||||
callingConvention = Procedure.CallingConvention.PHI_CALL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove extension from a file name if it is present.
|
||||
*
|
||||
* @param fileName The file name
|
||||
* @return file name without extension
|
||||
*/
|
||||
public static String removeFileNameExtension(String fileName) {
|
||||
final int lastDotIdx = fileName.lastIndexOf('.');
|
||||
final int lastSlashIdx = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
|
||||
if(lastDotIdx > 0 && lastDotIdx > (lastSlashIdx + 1)) {
|
||||
fileName = fileName.substring(0, lastDotIdx);
|
||||
}
|
||||
return fileName;
|
||||
// Find encoding
|
||||
StringEncoding encoding = program.getTargetPlatform().getEncoding();
|
||||
if(encoding==null)
|
||||
encoding = StringEncoding.SCREENCODE_MIXED;
|
||||
|
||||
// Find default interrupt type
|
||||
String interruptType = program.getTargetPlatform().getInterruptType();
|
||||
|
||||
Pass0GenerateStatementSequence pass0GenerateStatementSequence = new Pass0GenerateStatementSequence(cParser, cFileContext, program, callingConvention, encoding, interruptType);
|
||||
pass0GenerateStatementSequence.generate();
|
||||
|
||||
pass1GenerateSSA();
|
||||
pass2Optimize();
|
||||
pass2UnrollLoops();
|
||||
pass2InlineConstants();
|
||||
pass2FinalizeAllNumbers();
|
||||
|
||||
//getLog().append("\nCONTROL FLOW GRAPH PASS 2");
|
||||
//getLog().append(program.getGraph().toString(program));
|
||||
|
||||
//getLog().append("SYMBOL TABLE PASS 2");
|
||||
//getLog().append(program.getScope().toString(program, null));
|
||||
|
||||
pass3Analysis();
|
||||
pass4RegisterAllocation();
|
||||
pass5GenerateAndOptimizeAsm();
|
||||
}
|
||||
|
||||
private void pass1GenerateSSA() {
|
||||
|
37
src/main/java/dk/camelot64/kickc/FileNameUtils.java
Normal file
37
src/main/java/dk/camelot64/kickc/FileNameUtils.java
Normal file
@ -0,0 +1,37 @@
|
||||
package dk.camelot64.kickc;
|
||||
|
||||
/**
|
||||
* Utility functions for working with file names.
|
||||
*
|
||||
* Terminology
|
||||
* <ul>
|
||||
* <li>Path: A complete path to a file containing directory, file name and extension.
|
||||
* <br>Example <i>/usr/jg/note.txt</i></li>
|
||||
* <li>Directory: The part of a file name describing the directory containing the file.
|
||||
* <br>Example <i>/usr/jg/</i></li>
|
||||
* <li>File name: Part of the path describing the name of the file. The name has a base name and an optional '.' plus an extension.
|
||||
* <br>Example <i>note.txt</i></li>
|
||||
* <li>Base Name: The file name without any '.' and extension.
|
||||
* <br>Example <i>note</i></li>
|
||||
* <li>Extension: The extension of the file name. The contents after the last '.' in the file name.
|
||||
* <br>Example <i>txt</i></li>
|
||||
* </ul>
|
||||
*/
|
||||
public class FileNameUtils {
|
||||
|
||||
/**
|
||||
* Remove extension from a file name if it is present.
|
||||
*
|
||||
* @param fileName The file name
|
||||
* @return file name without extension
|
||||
*/
|
||||
public static String removeExtension(String fileName) {
|
||||
final int lastDotIdx = fileName.lastIndexOf('.');
|
||||
final int lastSlashIdx = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
|
||||
if(lastDotIdx > 0 && lastDotIdx > (lastSlashIdx + 1)) {
|
||||
fileName = fileName.substring(0, lastDotIdx);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
}
|
@ -238,6 +238,7 @@ public class KickC implements Callable<Integer> {
|
||||
final File platformFile = SourceLoader.loadFile(targetPlatform + "." + CTargetPlatformParser.FILE_EXTENSION, currentPath, program.getTargetPlatformPaths());
|
||||
final TargetPlatform targetPlatform = CTargetPlatformParser.parseTargetPlatformFile(this.targetPlatform, platformFile, currentPath, program.getTargetPlatformPaths());
|
||||
program.setTargetPlatform(targetPlatform);
|
||||
program.getOutputFileManager().setBinaryExtension(targetPlatform.getOutFileExtension());
|
||||
} catch(CompileError e) {
|
||||
// Print the error and exit with compile error
|
||||
System.err.println(e.getMessage());
|
||||
@ -252,6 +253,7 @@ public class KickC implements Callable<Integer> {
|
||||
// Update output file extension
|
||||
if(outputExtension != null) {
|
||||
program.getTargetPlatform().setOutFileExtension(outputExtension);
|
||||
program.getOutputFileManager().setBinaryExtension(outputExtension);
|
||||
}
|
||||
|
||||
// Update CPU
|
||||
@ -275,59 +277,38 @@ public class KickC implements Callable<Integer> {
|
||||
|
||||
if(cFiles != null && !cFiles.isEmpty()) {
|
||||
|
||||
program.getOutputFileManager().setCurrentDir(FileSystems.getDefault().getPath("."));
|
||||
|
||||
final Path primaryCFile = cFiles.get(0);
|
||||
String primaryFileBaseName = getFileBaseName(primaryCFile);
|
||||
program.getOutputFileManager().setPrimaryCFile(primaryCFile);
|
||||
|
||||
Path CFileDir = primaryCFile.getParent();
|
||||
if(CFileDir == null) {
|
||||
CFileDir = FileSystems.getDefault().getPath(".");
|
||||
}
|
||||
if(outputDir != null)
|
||||
program.getOutputFileManager().setOutputDir(outputDir);
|
||||
|
||||
if(outputDir == null) {
|
||||
outputDir = CFileDir;
|
||||
}
|
||||
if(!Files.exists(outputDir)) {
|
||||
Files.createDirectory(outputDir);
|
||||
}
|
||||
if(outputFileName!=null)
|
||||
program.getOutputFileManager().setOutputFileName(outputFileName);
|
||||
|
||||
String outputFileNameBase;
|
||||
if(outputFileName == null) {
|
||||
outputFileNameBase = primaryFileBaseName;
|
||||
} else {
|
||||
final int extensionIdx = outputFileName.lastIndexOf('.');
|
||||
if(extensionIdx > 0)
|
||||
outputFileNameBase = outputFileName.substring(0, extensionIdx);
|
||||
else
|
||||
outputFileNameBase = outputFileName;
|
||||
}
|
||||
|
||||
if(optimizeNoUplift) {
|
||||
if(optimizeNoUplift)
|
||||
compiler.setDisableUplift(true);
|
||||
}
|
||||
|
||||
if(optimizeUpliftCombinations != null) {
|
||||
if(optimizeUpliftCombinations != null)
|
||||
compiler.setUpliftCombinations(optimizeUpliftCombinations);
|
||||
}
|
||||
|
||||
if(optimizeZeroPageCoalesce) {
|
||||
if(optimizeZeroPageCoalesce)
|
||||
compiler.enableZeroPageCoalesce();
|
||||
}
|
||||
|
||||
if(optimizeLoopHeadConstant) {
|
||||
if(optimizeLoopHeadConstant)
|
||||
compiler.enableLoopHeadConstant();
|
||||
} else if(optimizeNoLoopHeadConstant) {
|
||||
else if(optimizeNoLoopHeadConstant)
|
||||
compiler.disableLoopHeadConstant();
|
||||
}
|
||||
|
||||
compiler.setEnableLiveRangeCallPath(optimizeLiveRangeCallPath);
|
||||
|
||||
if(warnFragmentMissing) {
|
||||
if(warnFragmentMissing)
|
||||
compiler.setWarnFragmentMissing(true);
|
||||
}
|
||||
|
||||
if(warnArrayType) {
|
||||
if(warnArrayType)
|
||||
compiler.setWarnArrayType(true);
|
||||
}
|
||||
|
||||
if(varModel != null) {
|
||||
List<String> settings = Arrays.asList(varModel.split(","));
|
||||
@ -367,6 +348,9 @@ public class KickC implements Callable<Integer> {
|
||||
effectiveDefines.putAll(program.getTargetPlatform().getDefines());
|
||||
program.addReservedZps(program.getTargetPlatform().getReservedZps());
|
||||
|
||||
if(assemble || execute || debug || emulator != null)
|
||||
program.getOutputFileManager().setAssembleOutput(true);
|
||||
|
||||
if(preprocess) {
|
||||
System.out.println("Preprocessing " + CFileNames);
|
||||
try {
|
||||
@ -388,8 +372,7 @@ public class KickC implements Callable<Integer> {
|
||||
return COMPILE_ERROR;
|
||||
}
|
||||
|
||||
String asmFileName = outputFileNameBase + ".asm";
|
||||
Path asmPath = outputDir.resolve(asmFileName);
|
||||
Path asmPath = program.getOutputFileManager().getAsmOutputFile();
|
||||
System.out.println("Writing asm file " + asmPath);
|
||||
FileOutputStream asmOutputStream = new FileOutputStream(asmPath.toFile());
|
||||
OutputStreamWriter asmWriter = new OutputStreamWriter(asmOutputStream);
|
||||
@ -402,13 +385,13 @@ public class KickC implements Callable<Integer> {
|
||||
program.getAsmFragmentMasterSynthesizer().finalize(compiler.getLog());
|
||||
|
||||
// Copy Resource Files (if out-dir is different from in-dir)
|
||||
if(!CFileDir.toAbsolutePath().equals(outputDir.toAbsolutePath())) {
|
||||
if(program.getOutputFileManager().shouldCopyResources()) {
|
||||
for(Path resourcePath : program.getAsmResourceFiles()) {
|
||||
Path outResourcePath = outputDir.resolve(resourcePath.getFileName().toString());
|
||||
Path outResourcePath = program.getOutputFileManager().getOutputDirectory().resolve(resourcePath.getFileName().toString());
|
||||
if(Files.exists(outResourcePath)) {
|
||||
FileTime resModified = Files.getLastModifiedTime(resourcePath);
|
||||
FileTime outModified = Files.getLastModifiedTime(outResourcePath);
|
||||
if(outModified.toMillis()>resModified.toMillis()) {
|
||||
if(outModified.toMillis() > resModified.toMillis()) {
|
||||
// Outfile is newer - move on to next file
|
||||
System.out.println("Resource already copied " + outResourcePath);
|
||||
continue;
|
||||
@ -422,8 +405,7 @@ public class KickC implements Callable<Integer> {
|
||||
}
|
||||
|
||||
// Assemble the asm-file if instructed
|
||||
String outputBinaryFileName = outputFileNameBase + "." + program.getTargetPlatform().getOutFileExtension();
|
||||
Path outputBinaryFilePath = outputDir.resolve(outputBinaryFileName);
|
||||
Path outputBinaryFilePath = program.getOutputFileManager().getBinaryOutputFile();
|
||||
|
||||
// Find emulator - if set by #pragma
|
||||
if(emulator == null) {
|
||||
@ -436,7 +418,7 @@ public class KickC implements Callable<Integer> {
|
||||
}
|
||||
|
||||
if(assemble || emulator != null) {
|
||||
Path kasmLogPath = outputDir.resolve(outputFileNameBase + ".klog");
|
||||
Path kasmLogPath = program.getOutputFileManager().getOutputFile("klog");
|
||||
System.out.println("Assembling to " + outputBinaryFilePath.toString());
|
||||
List<String> assembleCommand = new ArrayList<>();
|
||||
assembleCommand.add(asmPath.toString());
|
||||
@ -451,7 +433,7 @@ public class KickC implements Callable<Integer> {
|
||||
assembleCommand.add("-showmem");
|
||||
assembleCommand.add("-debugdump");
|
||||
// Add passed options
|
||||
if(assemblerOptions !=null)
|
||||
if(assemblerOptions != null)
|
||||
assembleCommand.addAll(assemblerOptions);
|
||||
|
||||
if(verbose) {
|
||||
@ -470,7 +452,7 @@ public class KickC implements Callable<Integer> {
|
||||
CharToPetsciiConverter.setCurrentEncoding("screencode_mixed");
|
||||
kasmResult = KickAssembler65CE02.main2(assembleCommand.toArray(new String[0]));
|
||||
} catch(Throwable e) {
|
||||
System.err.println("KickAssembling file failed! "+e.getMessage());
|
||||
System.err.println("KickAssembling file failed! " + e.getMessage());
|
||||
return COMPILE_ERROR;
|
||||
} finally {
|
||||
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
|
||||
@ -486,13 +468,13 @@ public class KickC implements Callable<Integer> {
|
||||
// Find commandline options for the emulator
|
||||
String emuOptions = "";
|
||||
if(emulator.equals("C64Debugger")) {
|
||||
Path viceSymbolsPath = outputDir.resolve(outputFileNameBase + ".vs");
|
||||
Path viceSymbolsPath = program.getOutputFileManager().getOutputFile("vs");
|
||||
emuOptions = "-symbols " + viceSymbolsPath + " -autojmp -prg ";
|
||||
}
|
||||
// The program names used by VICE emulators
|
||||
List<String> viceEmus = Arrays.asList("x64", "x64sc", "x128", "x64dtv", "xcbm2", "xcbm5x0", "xpet", "xplus4", "xscpu64", "xvic");
|
||||
if(viceEmus.contains(emulator)) {
|
||||
Path viceSymbolsPath = outputDir.resolve(outputFileNameBase + ".vs");
|
||||
Path viceSymbolsPath = program.getOutputFileManager().getOutputFile("vs");
|
||||
emuOptions = "-moncommands " + viceSymbolsPath.toAbsolutePath().toString() + " ";
|
||||
}
|
||||
System.out.println("Executing " + outputBinaryFilePath + " using " + emulator);
|
||||
@ -504,13 +486,13 @@ public class KickC implements Callable<Integer> {
|
||||
Process process = Runtime.getRuntime().exec(executeCommand);
|
||||
process.waitFor();
|
||||
} catch(Throwable e) {
|
||||
System.err.println("Executing emulator failed! "+e.getMessage());
|
||||
System.err.println("Executing emulator failed! " + e.getMessage());
|
||||
return COMPILE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(TmpDirManager.MANAGER!=null)
|
||||
if(TmpDirManager.MANAGER != null)
|
||||
TmpDirManager.MANAGER.cleanup();
|
||||
|
||||
return CommandLine.ExitCode.OK;
|
||||
@ -580,21 +562,6 @@ public class KickC implements Callable<Integer> {
|
||||
return new CommandLine(new KickC()).getCommandSpec().version()[0];
|
||||
}
|
||||
|
||||
static String getFileBaseName(Path file) {
|
||||
String name = file.getFileName().toString();
|
||||
int i = name.lastIndexOf('.');
|
||||
return i > 0 ? name.substring(0, i) : name;
|
||||
}
|
||||
|
||||
String getFileExtension(Path file) {
|
||||
if(file == null) {
|
||||
return "";
|
||||
}
|
||||
String name = file.getFileName().toString();
|
||||
int i = name.lastIndexOf('.');
|
||||
return i > 0 ? name.substring(i + 1) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Picocli Parameter Consumer for -D defines that allow both -Dname and -Dname=Value
|
||||
*/
|
||||
|
284
src/main/java/dk/camelot64/kickc/OutputFileManager.java
Normal file
284
src/main/java/dk/camelot64/kickc/OutputFileManager.java
Normal file
@ -0,0 +1,284 @@
|
||||
package dk.camelot64.kickc;
|
||||
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Handles output directories and files
|
||||
* The binary program output file name generated has the following file name components:
|
||||
* <br>
|
||||
* <i>output-path / output-basename . output-extension</i>
|
||||
* <p>
|
||||
* Where each component is determined in the following way:
|
||||
* <p>
|
||||
* <i>Output-path</i> is the directory where the output files are generated. The following is a prioritized list specifying how the compiler finds this folder:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed and the passed file name contains a directory part, then this directory is used.
|
||||
* <li>If the -odir command line option is passed, then this directory is used.
|
||||
* <li>If the first C-file passed to the compiler contains a directory part, then this directory is used.
|
||||
* <li>Otherwise the current directory is used.
|
||||
* </ol>
|
||||
* <p>
|
||||
* <i>Output-basename</i> is the file name of the output file without path and extension. The following is a prioritized list specifying how the compiler finds the output basename:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed, then the basename of the passed file name is used.
|
||||
* <li>The basename of the first C-file passed to the compiler is used.
|
||||
* </ol>
|
||||
* <p>
|
||||
* <i>Output-extension</i> is the file extension of the output file. The following is a prioritized list specifying how the compiler finds the output extension:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed and the compiler has been instructed to assemble the program (using -a or -e), then the extension of the passed file name is used.
|
||||
* <li>If the program contains #pragma extension(...) then the specified extension is used.
|
||||
* <li>If the -oext command line option is passed, then this extension is used.
|
||||
* <li>Otherwise the extension specified in the platform target (.tgt) file for the chosen platform is used.
|
||||
* </ol>
|
||||
* <p>
|
||||
* The ASM output file has the following file name components.
|
||||
* <br>
|
||||
* <i>output-path / output-basename asm-extension</i>
|
||||
* <p>
|
||||
* <i>Asm-extension</i> is the file extension of the ASM file. The following is a prioritized list specifying how the compiler finds the output extension:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed and the compiler has not been instructed to assemble the program (using -a or -e), then the extension of the passed file name is used.
|
||||
* <li></li>Otherwise the extension “asm” is used
|
||||
* </ol>
|
||||
*/
|
||||
public class OutputFileManager {
|
||||
|
||||
/** The output file name passed using -o */
|
||||
private String outputFileName;
|
||||
|
||||
/** The output directory name passed using -odir */
|
||||
private Path outputDir;
|
||||
|
||||
/** The current directory (where the compiler is executed). */
|
||||
private Path currentDir;
|
||||
|
||||
/** The primary (first) C-file passed to the compiler. */
|
||||
private Path primaryCFile;
|
||||
|
||||
/** The extension specified through #pragma extension, command line option -oext or the target platform TGT-file */
|
||||
private String binaryExtension;
|
||||
|
||||
/** Has the compiler been instructed to compile the output file. */
|
||||
private boolean assembleOutput;
|
||||
|
||||
public void setAssembleOutput(boolean assembleOutput) {
|
||||
this.assembleOutput = assembleOutput;
|
||||
}
|
||||
|
||||
public void setOutputFileName(String outputFileName) {
|
||||
this.outputFileName = outputFileName;
|
||||
}
|
||||
|
||||
public void setOutputDir(Path outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
}
|
||||
|
||||
public void setCurrentDir(Path currentDir) {
|
||||
this.currentDir = currentDir;
|
||||
}
|
||||
|
||||
public void setPrimaryCFile(Path primaryCFile) {
|
||||
this.primaryCFile = primaryCFile;
|
||||
}
|
||||
|
||||
public void setBinaryExtension(String binaryExtension) {
|
||||
this.binaryExtension = binaryExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the binary output file name with path and extension
|
||||
*
|
||||
* @return The binary output file
|
||||
*/
|
||||
public Path getBinaryOutputFile() {
|
||||
return getOutputFile(getOutputDirectory(), getBinaryExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output file name with a specific extension
|
||||
*
|
||||
* @return The output file with the specified extension
|
||||
*/
|
||||
public Path getAsmOutputFile() {
|
||||
return getOutputFile(getOutputDirectory(), getAsmExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output file name with a specific extension
|
||||
*
|
||||
* @return The output file with the specified extension
|
||||
*/
|
||||
public Path getOutputFile(String extension) {
|
||||
return getOutputFile(getOutputDirectory(), extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output file name in a specific directory with a specific extension
|
||||
*
|
||||
* @return The output file with the specified directory and extension
|
||||
*/
|
||||
public Path getOutputFile(Path outputDir, String extension) {
|
||||
String fileName = getOutputBaseName();
|
||||
if(extension.length() > 0) fileName += "." + extension;
|
||||
final Path outputFile = outputDir.resolve(fileName);
|
||||
return outputFile.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output directory
|
||||
* <i>Output-path</i> is the directory where the output files are generated. The following is a prioritized list specifying how the compiler finds this folder:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed and the passed file name contains a directory part, then this directory is used.
|
||||
* <li>If the -odir command line option is passed, then this directory is used.
|
||||
* <li>If the first C-file passed to the compiler contains a directory part, then this directory is used.
|
||||
* <li>Otherwise the current directory is used.
|
||||
* </ol>
|
||||
*
|
||||
* @return The output directory
|
||||
*/
|
||||
public Path getOutputDirectory() {
|
||||
// If the -o command line option is passed and the passed file name contains a directory part, then this directory is used.
|
||||
if(outputFileName != null) {
|
||||
final int lastSlashIdx = Math.max(outputFileName.lastIndexOf('/'), outputFileName.lastIndexOf('\\'));
|
||||
if(lastSlashIdx >= 0) {
|
||||
final String outputDirName = outputFileName.substring(0, lastSlashIdx);
|
||||
final Path outputFilePath = currentDir.resolve(outputDirName);
|
||||
if(!Files.exists(outputFilePath)) {
|
||||
try {
|
||||
Files.createDirectories(outputFilePath);
|
||||
} catch(IOException e) {
|
||||
throw new CompileError("Error creating output directory " + outputFilePath, e);
|
||||
}
|
||||
}
|
||||
return outputFilePath;
|
||||
}
|
||||
}
|
||||
// If the -odir command line option is passed, then this directory is used.
|
||||
if(outputDir != null) {
|
||||
if(!Files.exists(outputDir)) {
|
||||
try {
|
||||
Files.createDirectories(outputDir);
|
||||
} catch(IOException e) {
|
||||
throw new CompileError("Error creating output directory " + outputDir, e);
|
||||
}
|
||||
}
|
||||
return outputDir;
|
||||
}
|
||||
// If the first C-file passed to the compiler contains a directory part, then this directory is used.
|
||||
Path cFileDir = primaryCFile.getParent();
|
||||
if(cFileDir != null) {
|
||||
return cFileDir;
|
||||
}
|
||||
// Otherwise the current directory is used.
|
||||
return currentDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base name of the output file.
|
||||
* <i>Output-basename</i> is the file name of the output file without path and extension. The following is a prioritized list specifying how the compiler finds the output basename:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed, then the basename of the passed file name is used.
|
||||
* <li>The basename of the first C-file passed to the compiler is used.
|
||||
* </ol>
|
||||
*
|
||||
* @return The output file base name
|
||||
*/
|
||||
public String getOutputBaseName() {
|
||||
// If the -o command line option is passed, then the basename of the passed file name is used.
|
||||
if(outputFileName != null) {
|
||||
final Path outputFilePath = currentDir.resolve(outputFileName);
|
||||
return getBaseName(outputFilePath);
|
||||
}
|
||||
// The basename of the first C-file passed to the compiler is used.
|
||||
return getBaseName(primaryCFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base file name of a file without parent path and extension
|
||||
*
|
||||
* @param file The file path
|
||||
* @return The file base name
|
||||
*/
|
||||
private static String getBaseName(Path file) {
|
||||
String name = file.getFileName().toString();
|
||||
int i = name.lastIndexOf('.');
|
||||
return i > 0 ? name.substring(0, i) : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extension for the binary output file
|
||||
* <p>
|
||||
* <i>Output-extension</i> is the file extension of the output file. The following is a prioritized list specifying how the compiler finds the output extension:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed, then the extension of the passed file name is used.
|
||||
* <li>If the program contains #pragma extension(...) then the specified extension is used.
|
||||
* <li>If the -oext command line option is passed, then this extension is used.
|
||||
* <li>Otherwise the extension specified in the platform target (.tgt) file for the chosen platform is used.
|
||||
* </ol>
|
||||
*
|
||||
* @return The binary output file extension
|
||||
*/
|
||||
public String getBinaryExtension() {
|
||||
// If the -o command line option is passed, then the extension of the passed file name is used.
|
||||
if(assembleOutput && outputFileName != null) {
|
||||
final Path outputFilePath = currentDir.resolve(outputFileName);
|
||||
return getExtension(outputFilePath);
|
||||
}
|
||||
// If the program contains #pragma extension(...) then the specified extension is used.
|
||||
// If the -oext command line option is passed, then this extension is used.
|
||||
// Otherwise the extension specified in the platform target (.tgt) file for the chosen platform is used.
|
||||
return binaryExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extension of the ASM file.
|
||||
* <p>
|
||||
* <i>Asm-extension</i> is the file extension of the ASM file.
|
||||
* The following is a prioritized list specifying how the compiler finds the output extension:
|
||||
* <ol>
|
||||
* <li>If the -o command line option is passed and the compiler has not been instructed to assemble the program (using -a or -e), then the extension of the passed file name is used.
|
||||
* <li>Otherwise the extension “asm” is used
|
||||
* </ol>
|
||||
*
|
||||
* @return The extension for the ASM file
|
||||
*/
|
||||
public String getAsmExtension() {
|
||||
// If the -o command line option is passed and the compiler has not been instructed to assemble the program (using -a or -e), then the extension of the passed file name is used.
|
||||
if(!assembleOutput && outputFileName != null) {
|
||||
final Path outputFilePath = currentDir.resolve(outputFileName);
|
||||
return getExtension(outputFilePath);
|
||||
}
|
||||
// Otherwise the extension “asm” is used
|
||||
return "asm";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file name extension of a file
|
||||
*
|
||||
* @param file The file path
|
||||
* @return The file name extension
|
||||
*/
|
||||
private static String getExtension(Path file) {
|
||||
String name = file.getFileName().toString();
|
||||
int i = name.lastIndexOf('.');
|
||||
return i > 0 ? name.substring(i + 1) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the resource files should be copied.
|
||||
* The resources are copied if the primary C-file is not located in the output directory
|
||||
*
|
||||
* @return true if the resources should be copied
|
||||
*/
|
||||
public boolean shouldCopyResources() {
|
||||
final Path cFileDirectory = primaryCFile.getParent().toAbsolutePath();
|
||||
final Path outputDirectory = getOutputDirectory().toAbsolutePath();
|
||||
return !cFileDirectory.equals(outputDirectory);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package dk.camelot64.kickc.model;
|
||||
|
||||
import dk.camelot64.kickc.CompileLog;
|
||||
import dk.camelot64.kickc.OutputFileManager;
|
||||
import dk.camelot64.kickc.asm.AsmProgram;
|
||||
import dk.camelot64.kickc.fragment.AsmFragmentTemplateMasterSynthesizer;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
@ -21,8 +22,9 @@ public class Program {
|
||||
/** The log containing information about the compilation process. */
|
||||
private CompileLog log;
|
||||
|
||||
/** The name of the primary file being compiled. PASS 0-5 (STATIC) */
|
||||
private String primaryFileName;
|
||||
/** The manager responsible for creating output files. PASS 0-5 (STATIC) */
|
||||
private OutputFileManager outputFileManager;
|
||||
|
||||
/** Paths used for including files. PASS 0 (STATIC) */
|
||||
private List<String> includePaths;
|
||||
/** Paths used for library files. PASS 0 (STATIC) */
|
||||
@ -107,6 +109,7 @@ public class Program {
|
||||
private VariableRegisterWeights variableRegisterWeights;
|
||||
|
||||
public Program() {
|
||||
this.outputFileManager = new OutputFileManager();
|
||||
this.scope = new ProgramScope();
|
||||
this.log = new CompileLog();
|
||||
this.includePaths = new ArrayList<>();
|
||||
@ -176,6 +179,10 @@ public class Program {
|
||||
this.asm = null;
|
||||
}
|
||||
|
||||
public OutputFileManager getOutputFileManager() {
|
||||
return outputFileManager;
|
||||
}
|
||||
|
||||
public void setEnableLiveRangeCallPath(boolean enableLiveRangeCallPath) {
|
||||
this.enableLiveRangeCallPath = enableLiveRangeCallPath;
|
||||
}
|
||||
@ -481,13 +488,7 @@ public class Program {
|
||||
}
|
||||
|
||||
|
||||
public void setPrimaryFileName(String primaryFileName) {
|
||||
this.primaryFileName = primaryFileName;
|
||||
}
|
||||
|
||||
public String getPrimaryFileName() {
|
||||
return primaryFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a bunch of reserved zero-page addresses that the compiler is not allowed to use.
|
||||
|
@ -1,8 +1,7 @@
|
||||
package dk.camelot64.kickc.parser;
|
||||
|
||||
import dk.camelot64.kickc.Compiler;
|
||||
import dk.camelot64.kickc.FileNameUtils;
|
||||
import dk.camelot64.kickc.SourceLoader;
|
||||
import dk.camelot64.kickc.model.Comment;
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.TargetPlatform;
|
||||
@ -205,7 +204,7 @@ public class CParser {
|
||||
final TokenSource fileTokens = loadCFile(fileName, currentSourceFolderPath, program.getIncludePaths(), false);
|
||||
if(fileName.endsWith(".h")) {
|
||||
// The included file was a H-file - attempt to find the matching library C-file
|
||||
String libFileName = Compiler.removeFileNameExtension(fileName) + ".c";
|
||||
String libFileName = FileNameUtils.removeExtension(fileName) + ".c";
|
||||
final TokenSource cLibFileTokens = loadCFile(libFileName, currentSourceFolderPath, program.getLibraryPaths(), true);
|
||||
addSourceFirst(cLibFileTokens);
|
||||
}
|
||||
@ -266,6 +265,9 @@ public class CParser {
|
||||
define(macroName, program.getTargetPlatform().getDefines().get(macroName));
|
||||
// Add reserved ZP's from new platform
|
||||
program.addReservedZps(program.getTargetPlatform().getReservedZps());
|
||||
// Set the output file extension
|
||||
program.getOutputFileManager().setBinaryExtension(targetPlatform.getOutFileExtension());
|
||||
|
||||
} else {
|
||||
StringBuilder supported = new StringBuilder();
|
||||
final List<File> platformFiles = SourceLoader.listFiles(currentPath, program.getTargetPlatformPaths(), CTargetPlatformParser.FILE_EXTENSION);
|
||||
|
@ -227,6 +227,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
||||
case CParser.PRAGMA_EXTENSION:
|
||||
String extension = pragmaParamString(pragmaParamSingle(ctx));
|
||||
program.getTargetPlatform().setOutFileExtension(extension);
|
||||
program.getOutputFileManager().setBinaryExtension(extension);
|
||||
break;
|
||||
case CParser.PRAGMA_EMULATOR:
|
||||
String emuName = pragmaParamString(pragmaParamSingle(ctx));
|
||||
|
@ -90,7 +90,7 @@ public class Pass4CodeGeneration {
|
||||
asm.addLine(new AsmSetCpu(targetCpu));
|
||||
|
||||
String linkScriptBody = targetPlatform.getLinkScriptBody();
|
||||
String outputFileName = new File(program.getPrimaryFileName()).getName() + "." + program.getTargetPlatform().getOutFileExtension();
|
||||
String outputFileName = program.getOutputFileManager().getBinaryOutputFile().getFileName().toString();
|
||||
linkScriptBody = linkScriptBody.replace("%O", outputFileName);
|
||||
linkScriptBody = linkScriptBody.replace("%_O", outputFileName.toLowerCase());
|
||||
linkScriptBody = linkScriptBody.replace("%^O", outputFileName.toUpperCase());
|
||||
|
@ -10,7 +10,6 @@ import kickass.KickAssembler65CE02;
|
||||
import kickass.nonasm.c64.CharToPetsciiConverter;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.regex.Matcher;
|
||||
@ -55,11 +54,11 @@ public class Pass5FixLongBranches extends Pass5AsmOptimization {
|
||||
new Pass5ReindexAsmLines(getProgram()).optimize();
|
||||
Path tmpDir = TmpDirManager.MANAGER.newTmpDir();
|
||||
// Generate the ASM file
|
||||
String outputFileName = getProgram().getPrimaryFileName();
|
||||
try {
|
||||
//getLog().append("ASM");
|
||||
//getLog().append(getProgram().getAsm().toString(false, true));
|
||||
writeOutputFile(tmpDir, outputFileName, ".asm", getProgram().getAsm().toString(new AsmProgram.AsmPrintState(false), null));
|
||||
final Path asmFilePath = getProgram().getOutputFileManager().getOutputFile(tmpDir, getProgram().getOutputFileManager().getAsmExtension());
|
||||
writeOutputFile(asmFilePath, getProgram().getAsm().toString(new AsmProgram.AsmPrintState(false), null));
|
||||
// Copy Resource Files
|
||||
for(Path asmResourceFile : getProgram().getAsmResourceFiles()) {
|
||||
File binFile = getTmpFile(tmpDir, asmResourceFile.getFileName().toString());
|
||||
@ -70,8 +69,8 @@ public class Pass5FixLongBranches extends Pass5AsmOptimization {
|
||||
}
|
||||
|
||||
// Compile using KickAssembler - catch the output in a String
|
||||
File asmFile = getTmpFile(tmpDir, outputFileName, ".asm");
|
||||
File binaryFile = getTmpFile(tmpDir, outputFileName, "."+getProgram().getTargetPlatform().getOutFileExtension());
|
||||
File asmFile = getProgram().getOutputFileManager().getOutputFile(tmpDir, getProgram().getOutputFileManager().getAsmExtension()).toFile();
|
||||
File binaryFile = getProgram().getOutputFileManager().getOutputFile(tmpDir, getProgram().getOutputFileManager().getBinaryExtension()).toFile();
|
||||
ByteArrayOutputStream kickAssOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(kickAssOut));
|
||||
int asmRes = -1;
|
||||
@ -174,9 +173,9 @@ public class Pass5FixLongBranches extends Pass5AsmOptimization {
|
||||
}
|
||||
}
|
||||
|
||||
private File writeOutputFile(Path tmpDir, String fileName, String extension, String outputString) throws IOException {
|
||||
private File writeOutputFile(Path outputFile, String outputString) throws IOException {
|
||||
// Write output file
|
||||
File file = getTmpFile(tmpDir, fileName, extension);
|
||||
File file = outputFile.toFile();
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(outputStream);
|
||||
writer.write(outputString);
|
||||
@ -186,11 +185,6 @@ public class Pass5FixLongBranches extends Pass5AsmOptimization {
|
||||
return file;
|
||||
}
|
||||
|
||||
private static File getTmpFile(Path tmpDir, String fileName, String extension) {
|
||||
Path kcPath = FileSystems.getDefault().getPath(fileName);
|
||||
return new File(tmpDir.toFile(), kcPath.getFileName().toString() + extension);
|
||||
}
|
||||
|
||||
private static File getTmpFile(Path tmpDir, String fileName) {
|
||||
return new File(tmpDir.toFile(), fileName );
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package dk.camelot64.kickc.test;
|
||||
|
||||
import dk.camelot64.kickc.CompileLog;
|
||||
import dk.camelot64.kickc.*;
|
||||
import dk.camelot64.kickc.Compiler;
|
||||
import dk.camelot64.kickc.SourceLoader;
|
||||
import dk.camelot64.kickc.TmpDirManager;
|
||||
import dk.camelot64.kickc.asm.AsmProgram;
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
@ -18,10 +16,7 @@ import java.io.*;
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -166,6 +161,11 @@ public class TestPrograms {
|
||||
program.setTargetPlatform(targetPlatform);
|
||||
program.addReservedZps(program.getTargetPlatform().getReservedZps());
|
||||
|
||||
// Update the output file manager
|
||||
program.getOutputFileManager().setBinaryExtension(program.getTargetPlatform().getOutFileExtension());
|
||||
program.getOutputFileManager().setCurrentDir(FileSystems.getDefault().getPath("."));
|
||||
program.getOutputFileManager().setPrimaryCFile(filePath);
|
||||
|
||||
final Map<String, String> defines = new HashMap<>();
|
||||
defines.put("__KICKC__", "1");
|
||||
defines.putAll(program.getTargetPlatform().getDefines());
|
||||
@ -174,7 +174,7 @@ public class TestPrograms {
|
||||
compileAsm(fileName, program);
|
||||
boolean success = true;
|
||||
ReferenceHelper helper = new ReferenceHelperFolder(refPath);
|
||||
String baseFileName = Compiler.removeFileNameExtension(fileName);
|
||||
String baseFileName = FileNameUtils.removeExtension(fileName);
|
||||
success &= helper.testOutput(baseFileName, ".asm", program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, false, false), program));
|
||||
success &= helper.testOutput(baseFileName, ".sym", program.getScope().toString(program, false));
|
||||
success &= helper.testOutput(baseFileName, ".cfg", program.getGraph().toString(program));
|
||||
@ -189,7 +189,7 @@ public class TestPrograms {
|
||||
}
|
||||
|
||||
private void compileAsm(String fileName, Program program) throws IOException {
|
||||
String baseFileName = Compiler.removeFileNameExtension(fileName);
|
||||
String baseFileName = FileNameUtils.removeExtension(fileName);
|
||||
writeBinFile(baseFileName, ".asm", program.getAsm().toString(new AsmProgram.AsmPrintState(false), program));
|
||||
for(Path asmResourceFile : program.getAsmResourceFiles()) {
|
||||
File asmFile = getBinFile(baseFileName, ".asm");
|
||||
|
Loading…
x
Reference in New Issue
Block a user