Example #1
0
int
compiler_main(int argc, char* argv[])
{
  init_colors();
  init_symbols(syms);

  po::options_description common("common options");
  common.add_options()
    ("help",     "print help message")
    ("version",  "print version message")
    ("input,i",   po::value<String_seq>(), "specify build inputs")
    ("output,o",  po::value<String>(),     "specify the build output file")
    ("keep,k",    "keep temporary files");

  // FIXME: These really define the compilation mode.
  // Here are some rules:
  //
  //    -s implies -c
  //    -c implies no linking
  //
  //    -b [archive|library|program]
  //
  po::options_description compiler("compile options");
  compiler.add_options()
    ("assemble,s", "compile to native assembly")
    ("compile,c",  "compile but do not link")
    ("target,t",   po::value<String>()->default_value("program"),
                   "produce an archive, module, or program");

  po::positional_options_description pos;
  pos.add("input", -1);

  po::options_description all;
  all.add(common)
     .add(compiler);

  // Parse command line options.
  Config conf;
  po::variables_map vm;
  try {
    po::store(
      po::command_line_parser(argc, argv)
        .options(all)
        .positional(pos)
        .run(),
      vm);
    po::notify(vm);
  } catch(std::exception& err) {
    std::cerr << "error: " << err.what() << "\n\n";
    usage(std::cerr, all);
    return -1;
  }

  // Check for obvious flags first.
  if (vm.count("help")) {
    usage(std::cout, all);
    return 0;
  }
  if (vm.count ("version")) {
    // TODO: Generate the version number from the build.
    std::cout << "beaker v0.0" << '\n';
    return 0;
  }

  // Check options.
  if (vm.count("compile"))
    conf.compile = true;

  if (vm.count("assemble")) {
    conf.assemble = true;
    conf.compile = true;
  }

  if (vm.count("target")) {
    String t = vm["target"].as<String>();
    if (t == "program") {
      conf.target = program_tgt;
    } else if (t == "module") {
      conf.target = module_tgt;
    } else  {
      std::cerr << "error: invalid build target\n";
      usage(std::cerr, all);
      return -1;
    }
  }

  // Validate the input files.
  if (!vm.count("input")) {
    std::cerr << "error: no input files given\n";
    usage(std::cerr, all);
    return -1;
  }
  Path_seq inputs;
  for (String const& s : vm["input"].as<String_seq>())
    inputs.push_back(s);

  // Look for an output file. If not given, assume that
  // the end result is going to be a native binary. Note
  // that this is the end goal for inputs of any variety.
  String output;
  if (!vm.count("output")) {
    // TODO: This should depend on the compilation mode.
    output = "a.out";
  } else {
    output = vm["output"].as<String>();
  }

  // Parse all of the input files into the translation module.
  //
  // FIXME: We should collect a set of output files from
  // parsing since we could potentially pass .ll/.bc/.s/.o
  // files to the next phase of transdlation.
  //
  // FIXME: Clean up temporary files.
  Path ir = to_ir_file(output);
  if (!parse(inputs, ir, conf))
    return -1;

  Path as = to_asm_file(output);
  if (!lower(ir, as, conf))
    return -1;
  if (conf.assemble)
    return 0;

  Path obj = to_object_file(output);
  if (!assemble(as, obj, conf))
    return -1;
  if (conf.compile)
    return 0;

  // Generate the linked result.
  if (conf.target == program_tgt)
    return executable({obj}, output, conf);
  if (conf.target == module_tgt)
    return module({obj}, output, conf);

  return 0;
}
Example #2
0
int
translator_main(int argc, char* argv[])
{
  // Intitialize various subsystems.
  //
  // TODO: Always initialize parsing components?
  init_colors();
  init_symbols(syms);

  // TODO: Support extraction to other intermediate formats?
  // Maybe C, for example? Gimple?
  //
  // Note that an introspection tool would have a considerably
  // different interface.
  po::options_description common("options");
  common.add_options()
    ("help",     "print help message")
    ("version",  "print version message")
    ("input,i",   po::value<String>(), "specify the input file")
    ("output,o",  po::value<String>(), "specify the output file")
    ("keep,k",    "keep temporary files");

  po::positional_options_description pos;
  pos.add("input", 1);

  po::options_description all;
  all.add(common);

  // Parse command line options.
  po::variables_map vm;
  try {
    po::store(
      po::command_line_parser(argc, argv)
        .options(all)
        .positional(pos)
        .run(),
      vm);
    po::notify(vm);
  } catch(std::exception& err) {
    std::cerr << "error: " << err.what() << "\n\n";
    usage(std::cerr, all);
    return -1;
  }

  // Check for obvious flags first.
  if (vm.count("help")) {
    usage(std::cout, all);
    return 0;
  }

  if (vm.count ("version")) {
    // TODO: Generate the version number from the
    // build.
    std::cout << "beaker v0.0" << '\n';
    return 0;
  }

  // Validate the input.
  if (!vm.count("input")) {
    std::cerr << "error: no input file given\n";
    usage(std::cerr, all);
    return -1;
  }
  Path input = vm["input"].as<String>();


  // Look for an output file. If not given, the default
  // will be to produce .asm. Note that the default output
  // file is generated in the current directory.
  Path output;
  if (!vm.count("output"))
    output = to_asm_file(input.filename());
  else
    output = vm["output"].as<String>();

  // Compile the input into LLVM.
  Path temp;
  if (get_file_kind(input) == beaker_file) {
    temp = to_ir_file(input.filename());
    if (!compile(input, temp))
      return -1;
    input = temp;
  }

  // Lower llvm input to assembly.
  if (!lower(input, output))
    return -1;

  // Remove the temporary file.
  if (!temp.empty() && !vm.count("keep"))
    fs::remove(temp);

  return 0;
}