Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
425 lines (377 sloc) 10.5 KB
#! winxed
// winxedxc.winxed
// Winxed extern compiler
/*
This is the frontend. It parses options, load winxed compiler
and modules, call external compilers and linkers and eventually
executes the result.
This version works only with g++.
*/
$include_const "iglobals.pasm";
$include_const "libpaths.pasm";
$include "winxedxx.winxhead";
namespace WinxedXC
{
//**************************************************************
// Helper functions
function splitExtension(string filename)
{
/*
Get the extension from file name.
Return null string if no dot, empty string if the dot is the last
character.
*/
for (int pos = length(filename); pos > 0; --pos) {
string c = filename[pos - 1];
switch (c) {
case "/":
return filename, string(null);
case ".":
if (pos > 0)
return substr(filename, 0, pos - 1), substr(filename, pos);
}
}
return filename, string(null);
}
function is_relative(string path)
{
int l = length(path);
if (l == 0)
return true;
string c = path[0];
switch (c) {
case "/":
return false;
case ".":
if (l == 1)
return false;
c = path[1];
return c != "." && c != "/";
default:
return true;
}
}
function load_config(string filename)
{
var json = load_language("data_json");
var file = open(filename);
file.encoding("utf8");
var code = json.compile(file.readall());
file.close();
return code();
}
inline check_exit_code(int code) return int
{
return (code >> 8) & 0xFF;
}
//**************************************************************
function get_winxed_compiler()
{
// Load language module and compreg it, exit with error if not found.
var winxed = null;
try
winxed = load_language("winxed");
catch () { }
if (winxed == null) {
cry("Cannot load language");
exit(1);
}
return winxed;
}
function load_winxed_stage(string stage)
{
// Load and compreg a winxed stage
var module;
try
module = load_packfile(stage);
catch () { }
if (module == null) {
cry("Cannot load stage '", stage, "'");
exit(1);
}
try {
for (var loadsub in module.subs_by_tag("load"))
loadsub();
}
catch (e) {
cry("Error loading stage '", stage, "':");
cry(e.message);
exit(1);
}
var winxed;
try
winxed = new ["Winxed", "Compiler", "WinxedHLL" ];
catch () { }
if (winxed == null) {
cry("Winxed compiler not found in '", stage, "'");
exit(1);
}
return winxed;
}
class WinxedCompiler
{
var winxed;
function WinxedCompiler(string stage)
{
self.winxed = stage == null ?
get_winxed_compiler() :
load_winxed_stage(stage);
}
function version_string()
{
return self.winxed.version_string();
}
function compile(string sourcefile, var outhandle)
{
// Compile a winxed source to pir.
var parsed = self.winxed.compile_from_file(sourcefile,
"parse":[named("target")]
);
:WinxedXX.Output output(outhandle);
output.emit(parsed);
}
}
//**************************************************************
/*
C++ compiler wrapper.
Methods:
compile - compile a C++ source file to an object file.
link - link one or more object files with the winxedxx runtime.
*/
class CompilerCxx
{
var includedir;
var dynlib;
function CompilerCxx(var config)
{
if (config != null) {
var includedir = config["I"];
if (includedir != null)
self.includedir = includedir;
var dynlib = config["dynlib"];
if (dynlib != null)
self.dynlib = dynlib;
}
}
function compile(string cxxfile, string outfile)
{
string command[];
push(command, "g++");
push(command, "-Wall");
push(command, "-O2");
var includedir = self.includedir;
if (includedir != null)
push(command, "-I" + string(includedir));
push(command, "-fPIC");
push(command, "-o");
push(command, outfile);
push(command, "-c");
push(command, cxxfile);
int r = spawnw(command);
return check_exit_code(r);
}
function link(string outfile, var objs)
{
string args[] = [
"g++",
"-g",
"-o", outfile
];
for (string obj in objs)
push(args, obj);
if (self.dynlib != null)
push(args, self.dynlib);
push(args, "-ldl");
int r = spawnw(args);
return check_exit_code(r);
}
function linkshared(string outfile, var objs)
{
string args[] = [
"g++",
"-g",
"-shared",
"-o", outfile
];
for (string obj in objs)
push(args, obj);
if (self.dynlib != null)
push(args, self.dynlib);
push(args, "-ldl");
int r = spawnw(args);
return check_exit_code(r);
}
}
//**************************************************************
$load "Getopt/Obj.pbc";
class Options : ["Getopt", "Obj" ]
{
var name;
var options;
var opts;
function Options(var argv)
{
self.options = [
[ "o=s", "Object name" ],
[ "target=s", "Set target type: cxx, exe or run. Default is cxx." ],
[ "stage=s", "Use a compiler stage instead of default." ],
[ "I=s", "Add to parrot include search path" ],
[ "config=s", "Configuration file" ],
[ "verbose=i", "Talk a lot" ]
];
for (var o in self.options)
self.push_string(o[0]);
self.notOptStop(1);
self.name = argv.shift();
self.opts = self.get_options(argv);
}
function getstring(string option, string default_value[optional])
{
var value = self.opts[option];
return value != null ? string(value) : default_value;
}
function getint(string option, int default_value[optional])
{
var value = self.opts[option];
return value != null ? int(value) : default_value;
}
}
} // namespace WinxedXC
//**************************************************************
function main [main](var argv)
{
using namespace WinxedXC;
:Options cloptions(argv);
string target = cloptions.getstring("target", "run");
string outfile = cloptions.getstring("o");
string stage = cloptions.getstring("stage");
string incs = cloptions.getstring("I");
int verbose = cloptions.getint("verbose");
string configfile = cloptions.getstring("config");
// Load config from json file or use defaults.
var config = configfile != null ?
load_config(configfile) :
{
"cxx" : {
"dynlib" : "winxedxx.so",
"I" : "."
}
};
var configwinxed = config["winxed"];
if (configwinxed != null) {
string libs = configwinxed["L"];
if (libs != null) {
var interp = getinterp();
var lpaths = interp[IGLOBALS_LIB_PATHS];
var pathlibs = lpaths[PARROT_LIB_PATH_LIBRARY];
pathlibs.push(libs);
}
}
switch (target) {
case "cxx":
case "obj":
case "shared":
case "exe":
case "run":
break;
default:
cry("Invalid target: ", target);
exit(1);
}
int argc = elements(argv);
if (argc < 1)
throw Error("No source files");
if (incs != null) {
var interp = getinterp();
var lpaths = interp[IGLOBALS_LIB_PATHS];
var pathinc = lpaths[PARROT_LIB_PATH_INCLUDE];
pathinc.push(string(incs));
}
string cxxfiles[];
string objfiles[];
var winxed = null;
while (elements(argv) > 0) {
string sourcefile = argv.shift();
if (sourcefile == "--")
break;
:(string barename, string ext) = splitExtension(sourcefile);
switch (ext) {
case "cxx":
push(cxxfiles, sourcefile);
break;
case "o":
push(objfiles, sourcefile);
break;
case "winxed":
default:
if (winxed == null) {
if (verbose)
say("Loading winxed");
winxed = new WinxedXC.WinxedCompiler(stage);
if (verbose)
say("Using winxed ", winxed.version_string());
if (verbose)
say("Loading winxedxx");
load_bytecode("winxedxx.pbc");
}
if (verbose)
say("Compiling winxed: ", sourcefile);
string cxxfile = barename + ".cxx";
var cxxout = open(cxxfile, "w");
winxed.compile(sourcefile, cxxout);
cxxout.close();
push(cxxfiles, cxxfile);
break;
}
}
if (target == "cxx")
exit(0);
int r; // Result code from external calls
//--------------------------------------------------
// Compile the generated C++ to obj.
var cxxconfig = config["cxx"];
:CompilerCxx cxx(cxxconfig);
for (string cxxfile in cxxfiles) {
if (verbose)
say("Compiling C++: ", cxxfile);
string objfile = substr(cxxfile, 0, length(cxxfile) - 4) + ".o";
r = cxx.compile(cxxfile, objfile);
if (r) {
cry("Compile ", cxxfile, " failed");
exit(r);
}
push(objfiles, objfile);
}
if (target == "obj")
exit(0);
//--------------------------------------------------
// Link executable
if (outfile == null) {
// Let's be traditional:
outfile = "a.out";
}
if (verbose)
say("Linking: ", outfile);
if (target == "exe" || target == "run")
r = cxx.link(outfile, objfiles);
else
r = cxx.linkshared(outfile, objfiles);
if (r) {
cry("Link '", outfile, "' failed");
exit(r);
}
if (target == "exe" || target == "shared")
exit(0);
//--------------------------------------------------
// Run
if (verbose)
say("Run: ", outfile, " ", join(" ", argv));
if (is_relative(outfile))
outfile = "./" + outfile;
string cmd[] = [ outfile ];
for (string arg in argv)
push(cmd, arg);
r = check_exit_code(spawnw(cmd));
exit(r);
}
// End