/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Crown Copyright (c) 1997
*
* This TenDRA(r) Computer Program is subject to Copyright
* owned by the United Kingdom Secretary of State for Defence
* acting through the Defence Evaluation and Research Agency
* (DERA). It is made available to Recipients with a
* royalty-free licence for its use, reproduction, transfer
* to other parties and amendment for any purpose not excluding
* product development provided that any such use et cetera
* shall be deemed to be acceptance of the following conditions:-
*
* (1) Its Recipients shall ensure that this Notice is
* reproduced upon any copies or amended versions of it;
*
* (2) Any amended version of it shall be clearly marked to
* show both the nature of and the organisation responsible
* for the relevant amendment or amendments;
*
* (3) Its onward transfer from a recipient to another
* party shall be deemed to be that party's acceptance of
* these conditions;
*
* (4) DERA gives no warranty or assurance as to its
* quality or suitability for any purpose and DERA accepts
* no liability whatsoever in relation to any use to which
* it may be put.
*
* $TenDRA: tendra/src/tools/tcc/compile.c,v 1.10 2005/10/17 08:21:48 stefanf Exp $
*/
#include "config.h"
#include "msgcat.h"
#include "filename.h"
#include "list.h"
#include "archive.h"
#include "environ.h"
#include "flags.h"
#include "compile.h"
#include "execute.h"
#include "options.h"
#include "stages.h"
#include "startup.h"
#include "suffix.h"
#include "utility.h"
static filename *apply_unjoin (filename *, int);
/*
* CHECK IF ONE FILE IS THE C SPEC OF THE OTHER
*
* This macro checks if q is the C or C++ spec file corresponding to p.
* q must be of type C_SPEC and have the same uniq value as p.
*/
#define spec_of(p, q)\
((q) && ((q)->type == C_SPEC || (q)->type == CPP_SPEC) \
&& (q)->uniq == (p)->uniq)
/*
* APPLY THE SYSTEM COMPILER
*
* This routine applies the system C compiler to the files, input,
* returning the result.
*/
static filename *
apply_cc(filename *input)
{
filename *p = input;
filename *output = null;
while (p != null) {
filename *ps = null;
filename *pn = p->next;
p->next = null;
switch (p->type) {
case C_SOURCE : {
/* C source */
if (make_preproc || keeps [PREPROC_C]) {
p = do_cc (p, C_SOURCE, PREPROC_C);
if (stops [PREPROC_C]) break;
}
goto preproc_c_lab;
}
case PREPROC_C :
preproc_c_lab : {
/* Preprocessed C source */
if (use_system_cc == 1) {
ps = do_produce (p);
if (ps == null) {
p = null;
break;
} else {
ps = ps->next;
}
}
if (stops [DEP_TDF]) {
if (ps != null) p = null;
break;
} else if (stops [AS_SOURCE]) {
p = do_cc (p, C_SOURCE, AS_SOURCE);
break;
} else if (keeps [AS_SOURCE]) {
p = do_cc (p, C_SOURCE, AS_SOURCE);
} else {
p = do_cc (p, C_SOURCE, binary_obj_type);
break;
}
goto as_source_lab;
}
case CPP_SOURCE : {
/* C++ source */
if (make_preproc || keeps [PREPROC_CPP]) {
p = do_cc (p, CPP_SOURCE, PREPROC_CPP);
if (stops [PREPROC_CPP]) break;
}
goto preproc_cpp_lab;
}
case PREPROC_CPP :
preproc_cpp_lab : {
/* Preprocessed C++ source */
if (use_system_cc == 1) {
ps = do_cpp_produce (p);
if (ps == null) {
p = null;
break;
} else {
ps = ps->next;
}
}
if (stops [DEP_TDF]) {
if (ps != null) p = null;
break;
} else if (stops [AS_SOURCE]) {
p = do_cc (p, CPP_SOURCE, AS_SOURCE);
break;
} else if (keeps [AS_SOURCE]) {
p = do_cc (p, CPP_SOURCE, AS_SOURCE);
} else {
p = do_cc (p, CPP_SOURCE, binary_obj_type);
break;
}
goto as_source_lab;
}
case AS_SOURCE :
as_source_lab : {
/* Assembly source file */
if (!stops [AS_SOURCE]) p = do_cc (p, AS_SOURCE, BINARY_OBJ);
break;
}
case PREPROC_AS : {
/* Preprocessed assembly source */
p = do_cc (p, PREPROC_AS, AS_SOURCE);
goto as_source_lab;
}
case BINARY_OBJ : {
/* Binary object file */
break;
}
case C_SPEC : {
/* C spec file */
if (!allow_specs) {
MSG_is_a_C_spec_file (p->name);
p->type = DEFAULT_TYPE;
} else {
if (p->storage == INPUT_FILE &&
keeps_aux [BINARY_OBJ] == 1 &&
!stops [AS_SOURCE]) {
p = do_build_file (p, BINARY_OBJ);
}
}
break;
}
case CPP_SPEC : {
/* C++ spec file */
if (!allow_specs || !allow_cpp) {
MSG_is_a_cpp_spec_file (p->name);
p->type = DEFAULT_TYPE;
} else {
if (p->storage == INPUT_FILE &&
keeps_aux [BINARY_OBJ] == 1 &&
!stops [AS_SOURCE]) {
p = do_build_file (p, BINARY_OBJ);
}
}
break;
}
default : {
/* Other file types give an error */
MSG_TDF_file_not_recognised_in_cc_mode (p->name);
p->type = DEFAULT_TYPE;
break;
}
}
if (p && p->storage != INPUT_FILE && p->aux == null) {
boolean have_spec = 0;
filename *p_archive = p;
int t = p->type;
if (t == BINARY_OBJ && keeps [t] && binary_obj_type != t) {
if (spec_of (p_archive, ps)) {
p_archive->next = ps;
p_archive = p_archive->next;
ps = ps ->next;
p_archive->next = null;
have_spec = 1;
}
if (have_spec) {
p = do_build_file (p, BINARY_OBJ);
}
}
}
output = add_filename (output, p);
if (ps) output = add_filename (output, ps);
p = pn;
}
return (output);
}
/*
* APPLY THE TDF LINKER
*
* This routine applies the TDF linker to p, returning the result.
*/
static filename *
apply_tdf_link(filename *p)
{
static boolean tried = 0;
if (p == null || stops [INDEP_TDF]) return (p);
if (tokdef_name && tokdef_output == null && !tried) {
filename *q;
if (!allow_notation) read_env (TNC_ENV);
q = find_filename (tokdef_name, PRETTY_TDF);
q = do_notation (q);
if (q) tokdef_output = q->name;
tried = 1;
}
return (do_tdf_link (p));
}
/*
* APPLY THE MAIN COMPILATION STAGES
*
* This routine applies the main compilation phases to the input files
* input, and returns the corresponding output files. If produce is
* true then the compilation stops at the target independent TDF stage.
* This, and all the routines in this module, needs to be kept in step
* with Table 1.
*/
static filename *
apply_compile(filename *input, int produce)
{
filename *p = input;
filename *output = null;
if (use_system_cc) return (apply_cc (input));
while (p != null) {
filename *pc = p;
filename *pn = p->next;
p->next = null;
switch (p->type) {
case C_SOURCE : {
/* C source */
if (keeps [PREPROC_C]) {
p = do_preproc (p, PREPROC_C);
} else {
p = do_produce (p);
}
if (allow_specs && stops [C_SPEC]) p = null;
if (p != pc) p = apply_compile (p, produce);
break;
}
case PREPROC_C : {
/* Preprocessed C source */
if (stops [PREPROC_C]) break;
p = do_produce (p);
if (allow_specs && stops [C_SPEC]) p = null;
if (p != pc) p = apply_compile (p, produce);
break;
}
case CPP_SOURCE : {
/* C++ source */
if (allow_cpp) {
if (keeps [PREPROC_CPP]) {
p = do_cpp_preproc (p);
} else {
p = do_cpp_produce (p);
}
if (allow_specs && stops [CPP_SPEC]) p = null;
if (p != pc) p = apply_compile (p, produce);
} else {
MSG_is_a_cpp_source_file (p->name);
p->type = DEFAULT_TYPE;
}
break;
}
case PREPROC_CPP : {
/* Preprocessed C++ source */
if (stops [PREPROC_CPP]) break;
if (allow_cpp) {
p = do_cpp_produce (p);
if (allow_specs && stops [CPP_SPEC]) p = null;
if (p != pc) p = apply_compile (p, produce);
} else {
MSG_is_a_preprocessed_cpp_source_file (p->name);
p->type = DEFAULT_TYPE;
}
break;
}
case INDEP_TDF : {
/* Target independent TDF */
if (!produce) {
p = apply_tdf_link (p);
if (p != pc) p = apply_compile (p, produce);
}
break;
}
case DEP_TDF : {
/* Target dependent TDF */
if (!produce) {
p = do_translate (p);
if (p != pc) p = apply_compile (p, produce);
}
break;
}
case AS_SOURCE : {
/* Assembly source */
if (!produce) {
p = do_assemble (p);
if (p != pc) p = apply_compile (p, produce);
}
break;
}
case PREPROC_AS : {
/* Preprocessed assembly source */
if (!produce) {
p = do_preproc (p, AS_SOURCE);
p = apply_compile (p, produce);
}
break;
}
case PRETTY_TDF : {
/* TDF notation source */
if (allow_notation) {
p = do_notation (p);
if (p != pc) p = apply_compile (p, produce);
} else {
MSG_is_a_tdf_notation_source_file (p->name);
p->type = DEFAULT_TYPE;
}
break;
}
case PL_TDF : {
/* PL_TDF source */
if (allow_pl_tdf) {
p = do_pl_tdf (p);
if (p != pc) p = apply_compile (p, produce);
} else {
MSG_is_a_pl_tdf_source_file (p->name);
p->type = DEFAULT_TYPE;
}
break;
}
case C_SPEC : {
/* C spec file */
if (!allow_specs) {
MSG_is_a_C_spec_file (p->name);
p->type = DEFAULT_TYPE;
} else {
if (p->storage == INPUT_FILE &&
keeps_aux [BINARY_OBJ] == 1 &&
!stops [AS_SOURCE]) {
p = do_build_file (p, BINARY_OBJ);
}
}
break;
}
case CPP_SPEC : {
/* C++ spec file */
if (!allow_specs || !allow_cpp) {
MSG_is_a_cpp_spec_file (p->name);
p->type = DEFAULT_TYPE;
} else {
if (p->storage == INPUT_FILE &&
keeps_aux [BINARY_OBJ] == 1 &&
!stops [AS_SOURCE]) {
p = do_build_file (p, BINARY_OBJ);
}
}
break;
}
}
if (p && p->storage != INPUT_FILE && p->aux == null) {
boolean have_spec = 0;
filename *p_archive = p;
if (spec_of (p_archive, p_archive->next)) {
p_archive = p_archive->next;
have_spec = 1;
} else if (spec_of (p_archive, pn)) {
p_archive->next = pn;
p_archive = p_archive->next;
pn = pn->next;
p_archive->next = null;
have_spec = 1;
}
if (have_spec) {
int t = p->type;
if (t == INDEP_TDF && checker && !stops [t]) {
if (stops [BINARY_OBJ] ||
keeps_aux [BINARY_OBJ] == 1) {
p = do_build_file (p->next, BINARY_OBJ);
}
}
if (t == BINARY_OBJ && keeps [t]) {
p = do_build_file (p, BINARY_OBJ);
}
}
}
output = add_filename (output, p);
p = pn;
}
return (output);
}
/*
* FILTER OUT LIST OF BINARY OBJECT FILES
*
* This routine picks out the TDF capsules from the input files, input,
* compiles them to binary object files, and returns them.
*/
static filename *
filter_ofiles(filename *input)
{
filename *p = input;
filename *links = null;
while (p != null) {
filename *pn = p->next;
p->next = null;
p->aux = pn;
if (p->type == INDEP_TDF) {
filename *q = apply_compile (p, 0);
if (q && q->type == BINARY_OBJ) {
links = add_filename (links, q);
}
}
p = pn;
}
return (links);
}
/*
* APPLY THE SYSTEM LINKER
*
* This routine applies the system linker to the input files, input,
* and returns the corresponding output files.
*/
static filename *
apply_link(filename *input)
{
filename *p = input;
int spec_out = C_SPEC_2;
filename *links = null, *links_out = null;
filename *specs = null, *specs_out;
filename *others = null;
while (p != null) {
filename *pn = p->next;
p->next = null;
p->aux = pn;
if (p->type == BINARY_OBJ) {
links = add_filename (links, p);
} else if (p->type == C_SPEC) {
specs = add_filename (specs, p);
} else if (p->type == CPP_SPEC) {
specs = add_filename (specs, p);
spec_out = CPP_SPEC_2;
} else {
others = add_filename (others, p);
}
p = pn;
}
last_return = 0;
keeps [binary_obj_type] = 0;
specs_out = do_link_specs (specs, spec_out);
links = add_filename (links, filter_ofiles (specs_out));
if (use_dynlink != 0 && use_system_cc == 0) links = do_dynlink (links);
if (links) links_out = do_link (links);
if (specs_out && links_out) specs_out->uniq = links_out->uniq;
p = add_filename (links_out, specs_out);
p = add_filename (p, others);
if (last_return) {
/* If the linking failed, keep the .o files */
filename *q = input;
boolean b = keeps [BINARY_OBJ];
keeps [BINARY_OBJ] = 1;
p = null;
while (q != null) {
filename *qa = q->aux;
q->next = null;
if (q->type == C_SPEC || q->type == CPP_SPEC) {
q = null;
} else if (q->type == BINARY_OBJ) {
if (q->storage == INPUT_FILE || b) {
q = null;
} else if (allow_specs) {
if (link_specs) q = do_keep (q);
if (spec_of (q, qa)) {
q->next = qa;
qa->next = null;
qa = qa->aux;
if (link_specs) q->next = do_keep (q->next);
}
q = do_build_file (q, BINARY_OBJ);
} else {
q = do_keep (q);
}
}
p = add_filename (p, q);
q = qa;
}
}
return (p);
}
/*
* SPLIT ALL TDF ARCHIVES
*
* This routine splits any TDF archives in the input files, input,
* and returns the corresponding output files.
*/
static filename *
apply_split_arch(filename *input)
{
filename *p = input;
filename *output = null;
archive_type = TDF_ARCHIVE;
while (p != null) {
filename *pn = p->next;
p->next = null;
if (p->type == TDF_ARCHIVE) {
if (use_system_cc) {
MSG_is_a_tdf_archive (p->name);
p->type = DEFAULT_TYPE;
} else {
p = do_split_arch (p);
}
}
output = add_filename (output, p);
p = pn;
}
return (output);
}
/*
* BUILD A TDF ARCHIVE
*
* This routine creates a TDF archive from the input files, input,
* and returns the corresponding output files.
*/
static filename *
apply_build_arch(filename *input)
{
filename *p = input;
filename *links = null;
filename *specs = null;
filename *others = null;
int spec_out = C_SPEC_1;
while (p != null) {
filename *pn = p->next;
p->next = null;
if (p->type == INDEP_TDF) {
links = add_filename (links, p);
} else if (p->type == C_SPEC) {
specs = add_filename (specs, p);
} else if (p->type == CPP_SPEC) {
specs = add_filename (specs, p);
spec_out = CPP_SPEC_1;
} else {
others = add_filename (others, p);
}
p = pn;
}
specs = do_link_specs (specs, spec_out);
links = do_build_arch (links);
if (specs && links) specs->uniq = links->uniq;
p = add_filename (links, specs);
p = add_filename (p, others);
return (p);
}
/*
* BUILD A TDF COMPLEX
*
* This routine creates a complex TDF capsule from the input files,
* input, and returns the corresponding output files.
*/
static filename *
apply_build(filename *input)
{
filename *p = input;
filename *links = null;
filename *specs = null;
filename *others = null;
int spec_out = C_SPEC_1;
while (p != null) {
filename *pn = p->next;
p->next = null;
if (p->type == INDEP_TDF) {
links = add_filename (links, p);
} else if (p->type == C_SPEC) {
specs = add_filename (specs, p);
} else if (p->type == CPP_SPEC) {
specs = add_filename (specs, p);
spec_out = CPP_SPEC_1;
} else {
others = add_filename (others, p);
}
p = pn;
}
specs = do_link_specs (specs, spec_out);
links = do_tdf_build (links);
if (specs && links) specs->uniq = links->uniq;
p = add_filename (links, specs);
p = add_filename (p, others);
return (p);
}
/*
* APPLY THE PREPROCESSOR
*
* This routine applies the preprocessor to the input files, input,
* and returns the corresponding output files.
*/
static filename *
apply_preproc(filename *input)
{
filename *p = input;
filename *output = null;
if (use_system_cc) return (apply_cc (input));
while (p != null) {
filename *pn = p->next;
p->next = null;
if (p->type == C_SOURCE) {
p = do_preproc (p, PREPROC_C);
} else if (p->type == CPP_SOURCE) {
p = do_cpp_preproc (p);
}
output = add_filename (output, p);
p = pn;
}
return (output);
}
/*
* APPLY THE TDF PRETTY PRINTER
*
* This routine applies the TDF pretty printer to the input files,
* input, and returns the corresponding output files.
*/
static filename *
apply_pretty(filename *input)
{
filename *p = input;
filename *output = null;
while (p != null) {
filename *pn = p->next;
p->next = null;
if (make_tspec) {
if (p->type == C_SPEC) p = do_pretty (p);
} else if (p->type == INDEP_TDF) {
if (make_pretty == 2) p = apply_tdf_link (p);
p = do_pretty (p);
} else if (p->type == DEP_TDF) {
p = do_pretty (p);
}
output = add_filename (output, p);
p = pn;
}
return (output);
}
/*
* APPLY THE ARCHIVER TO UNJOIN C SPEC FILES
*
* This routine scans input for input archives of type t. It splits these
* into their component files. For internal archives the contents are
* in the aux field.
*/
static filename *
apply_unjoin(filename *input, int t)
{
filename *p = input;
filename *output = null;
archive_type = t;
while (p != null) {
filename *pn = p->next;
p->next = null;
if (p->type == t) {
if (p->storage == INPUT_FILE) {
if (is_archive (p->name)) p = do_split_arch (p);
} else if (p->aux != null) {
p = p->aux;
}
}
output = add_filename (output, p);
p = pn;
}
return (output);
}
/*
* APPLY ALL THE COMPILATION STAGES
*
* This routine applies all the compilation stages to the input files,
* input, and returns the corresponding output files.
*/
filename *
apply_all(filename *input)
{
filename *p = input;
/* Set up file types */
if (allow_specs) binary_obj_type = BINARY_OBJ_AUX;
/* Preprocessing is a special case */
if (make_preproc) return (apply_preproc (p));
/* Any TDF archives are split immediately */
p = apply_split_arch (input);
/* Deal with building TDF archive case */
if (make_archive) {
p = apply_compile (p, 1);
if (make_complex) p = apply_build (p);
return (apply_build_arch (p));
}
/* Deal with pretty printing case */
if (make_pretty) {
p = apply_compile (p, 1);
if (make_complex) p = apply_build (p);
return (apply_pretty (p));
}
/* Deal with building TDF complex */
if (make_complex) {
p = apply_compile (p, 1);
p = apply_build (p);
}
/* Main compilation phases */
p = apply_compile (p, (int) checker);
if (allow_specs && !stops [BINARY_OBJ]) {
p = apply_unjoin (p, BINARY_OBJ);
}
p = apply_link (p);
return (p);
}
syntax highlighted by Code2HTML, v. 0.9.1