blob: 13e4abe3c13016ed2acbc1ef8b0f97c4acdd610b [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2002,2003 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
***********************************************************************/
package org.eclipse.cdt.make.internal.core.makefile.posix;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import org.eclipse.cdt.make.core.MakeCorePlugin;
import org.eclipse.cdt.make.core.makefile.IDirective;
import org.eclipse.cdt.make.internal.core.makefile.AbstractMakefile;
import org.eclipse.cdt.make.internal.core.makefile.BadDirective;
import org.eclipse.cdt.make.internal.core.makefile.Command;
import org.eclipse.cdt.make.internal.core.makefile.Comment;
import org.eclipse.cdt.make.internal.core.makefile.DefaultRule;
import org.eclipse.cdt.make.internal.core.makefile.Directive;
import org.eclipse.cdt.make.internal.core.makefile.EmptyLine;
import org.eclipse.cdt.make.internal.core.makefile.IgnoreRule;
import org.eclipse.cdt.make.internal.core.makefile.InferenceRule;
import org.eclipse.cdt.make.internal.core.makefile.MacroDefinition;
import org.eclipse.cdt.make.internal.core.makefile.MakefileReader;
import org.eclipse.cdt.make.internal.core.makefile.PosixRule;
import org.eclipse.cdt.make.internal.core.makefile.PreciousRule;
import org.eclipse.cdt.make.internal.core.makefile.Rule;
import org.eclipse.cdt.make.internal.core.makefile.SccsGetRule;
import org.eclipse.cdt.make.internal.core.makefile.SilentRule;
import org.eclipse.cdt.make.internal.core.makefile.SpecialRule;
import org.eclipse.cdt.make.internal.core.makefile.SuffixesRule;
import org.eclipse.cdt.make.internal.core.makefile.Target;
import org.eclipse.cdt.make.internal.core.makefile.TargetRule;
import org.eclipse.cdt.make.internal.core.makefile.Util;
import org.eclipse.core.runtime.Path;
/**
* Makefile : ( statement ) *
* statement : rule | macro_definition | comments | empty
* rule : inference_rule | target_rule | special_rule
* inference_rule : target ':' [ ';' command ] <nl>
[ ( command ) * ]
* target_rule : [ ( target ) + ] ':' [ ( prerequisite ) * ] [ ';' command ] <nl>
[ ( command ) * ]
* macro_definition : string '=' ( string )*
* comments : ('#' ( string ) <nl>) *
* empty : <nl>
* command : <tab> prefix_command string <nl>
* target : string
* prefix_command : '-' | '@' | '+'
* internal_macro : "$<" | "$*" | "$@" | "$?" | "$%"
*/
public class PosixMakefile extends AbstractMakefile {
IDirective[] builtins = null;
public PosixMakefile() {
super(null);
}
public void parse(String name) throws IOException {
FileReader stream = new FileReader(name);
parse(stream);
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
public void parse(Reader reader) throws IOException {
parse(new MakefileReader(reader));
}
protected void parse(MakefileReader reader) throws IOException {
String line;
Rule[] rules = null;
int startLine = 0;
int endLine = 0;
while ((line = reader.readLine()) != null) {
startLine = endLine + 1;
endLine = reader.getLineNumber();
// 1- Try command first, since we can not strip '#' in command line
if (PosixMakefileUtil.isCommand(line)) {
Command cmd = new Command(this, line);
cmd.setLines(startLine, endLine);
// The command is added to the rules
if (rules != null) {
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(cmd);
rules[i].setEndLine(endLine);
}
continue;
}
// If we have no rules for the command,
// give the other directives a chance by falling through
}
// 2- Strip away any comments.
int pound = Util.indexOfComment(line);
if (pound != -1) {
Comment cmt = new Comment(this, line.substring(pound + 1));
cmt.setLines(startLine, endLine);
if (rules != null) {
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(cmt);
rules[i].setEndLine(endLine);
}
} else {
addDirective(cmt);
}
line = line.substring(0, pound);
// If all we have left are spaces continue
if (Util.isEmptyLine(line)) {
continue;
}
// The rest of the line maybe a valid directive.
// keep on trying by falling through.
}
// 3- Empty lines ?
if (Util.isEmptyLine(line)) {
Directive empty = new EmptyLine(this);
empty.setLines(startLine, endLine);
if (rules != null) {
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(empty);
rules[i].setEndLine(endLine);
}
} else {
addDirective(empty);
}
continue;
}
// 4- reset the rules to null
// The first non empty line that does not begin with a <TAB> or '#'
// shall begin a new entry.
rules = null;
// 5- Check for the special rules.
SpecialRule special = processSpecialRule(line);
if (special != null) {
rules = new Rule[] { special };
special.setLines(startLine, endLine);
addDirective(special);
continue;
}
// 6- Check for inference rule.
if (PosixMakefileUtil.isInferenceRule(line)) {
InferenceRule irule = parseInferenceRule(line);
irule.setLines(startLine, endLine);
addDirective(irule);
rules = new Rule[]{irule};
continue;
}
// 7- Macro Definiton ?
if (PosixMakefileUtil.isMacroDefinition(line)) {
Directive stmt = parseMacroDefinition(line);
stmt.setLines(startLine, endLine);
addDirective(stmt);
continue;
}
// 8- Target Rule ?
if (PosixMakefileUtil.isTargetRule(line)) {
TargetRule[] trules = parseTargetRule(line);
for (int i = 0; i < trules.length; i++) {
trules[i].setLines(startLine, endLine);
addDirective(trules[i]);
}
rules = trules;
continue;
}
// XXX ?? Should not be here.
BadDirective stmt = new BadDirective(this, line);
stmt.setLines(startLine, endLine);
addDirective(stmt);
}
setLines(1, endLine);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.make.internal.core.makefile.AbstractMakefile#getBuiltins()
*/
public IDirective[] getBuiltins() {
if (builtins == null) {
String location = "builtin" + File.separator + "posix.mk";
try {
InputStream stream = MakeCorePlugin.getDefault().openStream(new Path(location));
PosixMakefile gnu = new PosixMakefile();
gnu.parse(new InputStreamReader(stream));
builtins = gnu.getDirectives();
for (int i = 0; i < builtins.length; i++) {
if (builtins[i] instanceof MacroDefinition) {
((MacroDefinition)builtins[i]).setFromDefault(true);
}
}
} catch (Exception e) {
//e.printStackTrace();
}
if (builtins == null) {
builtins = new IDirective[0];
}
}
return builtins;
}
/**
* @param line
* @return
*/
protected SpecialRule processSpecialRule(String line) {
line = line.trim();
String keyword = null;
String[] reqs = null;
SpecialRule special = null;
int index = Util.indexOf(line, ':');
if (index != -1) {
keyword = line.substring(0, index).trim();
String req = line.substring(index + 1);
reqs = PosixMakefileUtil.findPrerequisites(req);
} else {
keyword = line;
reqs = new String[0];
}
if (".IGNORE".equals(keyword)) {
special = new IgnoreRule(this, reqs);
} else if (".POSIX".equals(keyword)) {
special = new PosixRule(this);
} else if (".PRECIOUS".equals(keyword)) {
special = new PreciousRule(this, reqs);
} else if (".SILENT".equals(keyword)) {
special = new SilentRule(this, reqs);
} else if (".SUFFIXES".equals(keyword)) {
special = new SuffixesRule(this, reqs);
} else if (".DEFAULT".equals(keyword)) {
special = new DefaultRule(this, new Command[0]);
} else if (".SCCS_GET".equals(keyword)) {
special = new SccsGetRule(this, new Command[0]);
}
return special;
}
/**
* Inference Rule
*/
protected InferenceRule parseInferenceRule(String line) {
String tgt;
int index = Util.indexOf(line, ':');
if (index != -1) {
tgt = line.substring(0, index);
} else {
tgt = line;
}
return new InferenceRule(this, new Target(tgt));
}
/**
* MacroDefinition
*/
protected MacroDefinition parseMacroDefinition(String line) {
String name;
String value;
int index = Util.indexOf(line, '=');
if (index != -1) {
name = line.substring(0, index).trim();
value = line.substring(index + 1).trim();
} else {
name = line;
value = "";
}
return new MacroDefinition(this, name, new StringBuffer(value));
}
/**
* TargetRule
*/
protected TargetRule[] parseTargetRule(String line) {
String[] targets;
String[] reqs;
String cmd = null;
int index = Util.indexOf(line, ':');
if (index != -1) {
String target = line.substring(0, index);
// Tokenize the targets
targets = PosixMakefileUtil.findTargets(target);
String req = line.substring(index + 1);
int semicolon = Util.indexOf(req, ';');
if (semicolon != -1) {
String c = req.substring(semicolon + 1).trim();
if (c.length() > 0) {
cmd = c;
}
req = req.substring(0, semicolon);
}
reqs = PosixMakefileUtil.findPrerequisites(req);
} else {
targets = PosixMakefileUtil.findTargets(line);
reqs = new String[0];
}
TargetRule[] targetRules = new TargetRule[targets.length];
for (int i = 0; i < targets.length; i++) {
targetRules[i] = new TargetRule(this, new Target(targets[i]), reqs);
if (cmd != null) {
Command command = new Command(this, cmd);
targetRules[i].addDirective(command);
}
}
return targetRules;
}
public static void main(String[] args) {
try {
String filename = "Makefile";
if (args.length == 1) {
filename = args[0];
}
PosixMakefile makefile = new PosixMakefile();
makefile.parse(filename);
IDirective[] directives = makefile.getDirectives();
//IDirective[] directives = makefile.getBuiltins();
for (int i = 0; i < directives.length; i++) {
//System.out.println("Rule[" + i +"]");
System.out.print(directives[i]);
}
} catch (IOException e) {
System.out.println(e);
}
}
}