/*******************************************************************************
* Copyright (c) 2000, 2015 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Red Hat Inc. - Modified for Automake editor usage
*******************************************************************************/
package org.eclipse.cdt.internal.autotools.ui.editors.automake;
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 java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;
import org.eclipse.cdt.autotools.ui.AutotoolsUIPlugin;
import org.eclipse.cdt.make.core.MakeCorePlugin;
import org.eclipse.cdt.make.core.makefile.IAutomaticVariable;
import org.eclipse.cdt.make.core.makefile.IBuiltinFunction;
import org.eclipse.cdt.make.core.makefile.IDirective;
import org.eclipse.cdt.make.core.makefile.IMakefile;
import org.eclipse.cdt.make.core.makefile.gnu.IGNUMakefile;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
/**
* Makefile : ( statement ) *
* statement : rule | macro_definition | comments | empty
* rule : inference_rule | target_rule
* inference_rule : target ':' <nl> ( <tab> command <nl> ) +
* target_rule : target [ ( 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 GNUAutomakefile extends AbstractMakefile implements IGNUMakefile {
public static final String PATH_SEPARATOR = System.getProperty("path.separator", ":"); //$NON-NLS-1$ //$NON-NLS-2$
public static final String FILE_SEPARATOR = System.getProperty("file.separator", "/"); //$NON-NLS-1$ //$NON-NLS-2$
String[] includeDirectories = new String[0];
IDirective[] builtins = null;
public GNUAutomakefile() {
super(null);
}
public void parse(String name) throws IOException {
try (FileReader stream = new FileReader(name)) {
parse(name, stream);
}
}
@Override
public void parse(String filePath, Reader reader) throws IOException {
parse(URIUtil.toURI(filePath), new MakefileReader(reader));
}
@Override
public void parse(URI fileURI, Reader reader) throws IOException {
parse(fileURI, new MakefileReader(reader));
}
protected void parse(URI fileURI, MakefileReader reader) throws IOException {
String line;
Rule[] rules = null;
Stack<IDirective> conditions = new Stack<>();
Stack<GNUVariableDef> defines = new Stack<>();
int startLine = 0;
int endLine = 0;
// Clear any old directives.
clearDirectives();
setFileURI(fileURI);
while ((line = reader.readLine()) != null) {
startLine = endLine + 1;
endLine = reader.getLineNumber();
// Check if we enter in "define"
if (GNUMakefileUtil.isEndef(line)) {
// We should have a "define" for a "endef".
if (!defines.empty()) {
GNUVariableDef def = defines.pop();
def.setEndLine(endLine);
}
Endef endef = new Endef(this);
endef.setLines(startLine, endLine);
addDirective(conditions, endef);
continue;
} else if (GNUMakefileUtil.isDefine(line)) {
GNUVariableDef def = parseGNUVariableDef(line);
def.setLines(startLine, endLine);
addDirective(conditions, def);
defines.push(def);
continue;
} else if (GNUMakefileUtil.isOverrideDefine(line)) {
GNUVariableDef oDef = parseGNUVariableDef(line);
oDef.setLines(startLine, endLine);
addDirective(conditions, oDef);
defines.push(oDef);
continue;
}
// We still in a define.
if (!defines.empty()) {
GNUVariableDef def = defines.peek();
StringBuffer sb = def.getValue();
if (sb.length() > 0) {
sb.append('\n');
}
sb.append(line);
continue;
}
// 1- Try command first, since we can not strip '#' in command line
if (PosixMakefileUtil.isCommand(line) ||
AutomakefileUtil.isAutomakeCommand(line)) {
Command cmd = new Command(this, line);
cmd.setLines(startLine, endLine);
if (rules != null) {
// The command is added to the rules
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(cmd);
rules[i].setEndLine(endLine);
}
continue;
}
if (!conditions.isEmpty()) {
addDirective(conditions, cmd);
continue;
}
// If we have no rules/condition 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) {
// The comment is added to the rules.
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(cmt);
rules[i].setEndLine(endLine);
}
} else {
addDirective(conditions, cmt);
}
line = line.substring(0, pound);
// If all we have left are spaces continue
if (line.trim().isEmpty()) {
continue;
}
// The rest of the line maybe a valid directives.
// keep on trying by falling through.
}
// 3- Empty lines ?
if (line.trim().isEmpty()) {
Directive empty = new EmptyLine(this);
empty.setLines(startLine, endLine);
if (rules != null) {
// The EmptyLine is added to the rules.
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(empty);
rules[i].setEndLine(endLine);
}
} else {
addDirective(conditions, empty);
}
continue;
}
// 3b - look for if statement which is an Automake directive.
// These will be processed at configure time and all statements
// in-between are modified with a prefix. We will treat these as
// standard conditionals even though they can affect the behavior
// of lines that follow the construct. For
// example, an automake-if could start in the middle of a rule
// and the else might be outside a target.
if (GNUMakefileUtil.isIf(line)) {
// We cache the rules that were in effect at entry to the
// if/else block. We treat the endif as restoring the
// state of the rules at this point. It is possible for
// commands to follow the if/else that belong to different
// rules that start inside the if/else, but we are not
// prepared to handle that in an outline view and it is
// pretty bad Automake coding.
If ifDirective = parseIf(line, rules);
ifDirective.setLines(startLine, endLine);
if (rules != null) {
// The if statement is added to the rules.
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(ifDirective);
rules[i].setEndLine(endLine);
}
} else {
addDirective(conditions, ifDirective);
}
conditions.push(ifDirective);
rules = null;
continue;
}
// 3c - Check for else or endif
if (GNUMakefileUtil.isElse(line)) {
Else elseDirective = (Else)parseConditional(line);
elseDirective.setLines(startLine, endLine);
Conditional cond = null;
// FIXME: Are we missing a if condition ?
if (!conditions.empty()) {
cond = (Conditional) conditions.pop();
cond.setEndLine(endLine - 1);
}
if (cond != null && cond.isIf()) {
// See 3b above for description on automake if/else handling.
elseDirective.setAutomake(true);
rules = ((If)cond).getRules();
// We cache the rules at the time of entry into the if/else block.
elseDirective.setRules(rules);
if (rules != null) {
// The else is added to the rules.
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(elseDirective);
rules[i].setEndLine(endLine);
}
} else {
addDirective(conditions, elseDirective);
}
rules = null;
} else {
addDirective(conditions, elseDirective);
}
conditions.push(elseDirective);
continue;
} else if (GNUMakefileUtil.isEndif(line)) {
Endif endif = new Endif(this);
endif.setLines(startLine, endLine);
Conditional cond = null;
// FIXME: Are we missing a if/else condition ?
if (!conditions.empty()) {
cond = (Conditional) conditions.pop();
cond.setEndLine(endLine);
}
if (cond instanceof IAutomakeConditional) {
rules = ((IAutomakeConditional)cond).getRules();
}
if (rules != null) {
// The endif is added to the rules.
for (int i = 0; i < rules.length; i++) {
rules[i].addDirective(endif);
rules[i].setEndLine(endLine);
}
} else {
addDirective(conditions, endif);
}
continue;
}
// 4 - reset 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 conditionals.
Directive directive = processConditions(line);
if (directive != null) {
directive.setLines(startLine, endLine);
addDirective(conditions, directive);
conditions.push(directive);
continue;
}
// 6- Check for other special gnu directives.
directive = processGNUDirectives(line);
if (directive != null) {
directive.setLines(startLine, endLine);
addDirective(conditions, directive);
continue;
}
// 7- Check for GNU special rules.
SpecialRule special = processSpecialRules(line);
if (special != null) {
rules = new Rule[] { special };
special.setLines(startLine, endLine);
addDirective(conditions, special);
continue;
}
// - Check for inference rule.
if (PosixMakefileUtil.isInferenceRule(line)) {
InferenceRule irule = parseInferenceRule(line);
irule.setLines(startLine, endLine);
addDirective(conditions, irule);
rules = new Rule[] { irule };
continue;
}
// - Variable Definiton ?
if (GNUMakefileUtil.isVariableDefinition(line)) {
GNUVariableDef vd = parseGNUVariableDef(line);
vd.setLines(startLine, endLine);
addDirective(conditions, vd);
if (!vd.isTargetSpecific()) {
continue;
}
}
// - GNU Static Target rule ?
if (GNUMakefileUtil.isStaticTargetRule(line)) {
StaticTargetRule[] srules = parseStaticTargetRule(line);
for (int i = 0; i < srules.length; i++) {
srules[i].setLines(startLine, endLine);
addDirective(conditions, srules[i]);
}
rules = srules;
continue;
}
// - Target Rule ?
if (GNUMakefileUtil.isGNUTargetRule(line)) {
GNUTargetRule[] trules = parseGNUTargetRules(line);
for (int i = 0; i < trules.length; i++) {
trules[i].setLines(startLine, endLine);
addDirective(conditions, trules[i]);
}
rules = trules;
continue;
}
// - Configure macro (@xxxx@)
if (AutomakefileUtil.isConfigMacro(line)) {
AutomakeConfigMacro macro = parseConfigMacro(line);
if (macro != null) {
macro.setLines(startLine, endLine);
addDirective(macro);
continue;
}
}
// XXX ?? Should not be here.
BadDirective stmt = new BadDirective(this, line);
stmt.setLines(startLine, endLine);
addDirective(conditions, stmt);
}
setLines(1, endLine);
// TEST please remove.
//GNUMakefileValidator validator = new GNUMakefileValidator();
//validator.validateDirectives(null, getDirectives());
}
private void addDirective(Stack<IDirective> conditions, Directive directive) {
if (conditions.empty()) {
addDirective(directive);
} else {
Conditional cond = (Conditional) conditions.peek();
cond.addDirective(directive);
cond.setEndLine(directive.getEndLine());
}
}
protected Conditional processConditions(String line) {
Conditional stmt = null;
if (GNUMakefileUtil.isIfdef(line)) {
stmt = parseConditional(line);
} else if (GNUMakefileUtil.isIfndef(line)) {
stmt = parseConditional(line);
} else if (GNUMakefileUtil.isIfeq(line)) {
stmt = parseConditional(line);
} else if (GNUMakefileUtil.isIfneq(line)) {
stmt = parseConditional(line);
}
return stmt;
}
protected Directive processGNUDirectives(String line) {
Directive stmt = null;
if (GNUMakefileUtil.isUnExport(line)) {
stmt = parseUnExport(line);
} else if (GNUMakefileUtil.isVPath(line)) {
stmt = parseVPath(line);
} else if (GNUMakefileUtil.isInclude(line)) {
stmt = parseInclude(line);
}
return stmt;
}
protected SpecialRule processSpecialRules(String line) {
SpecialRule stmt = null;
if (PosixMakefileUtil.isIgnoreRule(line)) {
stmt = parseSpecialRule(line);
} else if (PosixMakefileUtil.isPosixRule(line)) {
stmt = parseSpecialRule(line);
} else if (PosixMakefileUtil.isPreciousRule(line)) {
stmt = parseSpecialRule(line);
} else if (PosixMakefileUtil.isSilentRule(line)) {
stmt = parseSpecialRule(line);
} else if (PosixMakefileUtil.isSuffixesRule(line)) {
stmt = parseSpecialRule(line);
} else if (PosixMakefileUtil.isDefaultRule(line)) {
stmt = parseSpecialRule(line);
} else if (PosixMakefileUtil.isSccsGetRule(line)) {
stmt = parseSpecialRule(line);
} else if (GNUMakefileUtil.isPhonyRule(line)) {
stmt = parseSpecialRule(line);
} else if (GNUMakefileUtil.isIntermediateRule(line)) {
stmt = parseSpecialRule(line);
} else if (GNUMakefileUtil.isSecondaryRule(line)) {
stmt = parseSpecialRule(line);
} else if (GNUMakefileUtil.isDeleteOnErrorRule(line)) {
stmt = parseSpecialRule(line);
} else if (GNUMakefileUtil.isLowResolutionTimeRule(line)) {
stmt = parseSpecialRule(line);
} else if (GNUMakefileUtil.isExportAllVariablesRule(line)) {
stmt = parseSpecialRule(line);
} else if (GNUMakefileUtil.isNotParallelRule(line)) {
stmt = parseSpecialRule(line);
}
return stmt;
}
/**
* @param line
* @return
*/
protected SpecialRule parseSpecialRule(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 (keyword.equals(MakeFileConstants.RULE_IGNORE)) {
special = new IgnoreRule(this, reqs);
} else if (keyword.equals(MakeFileConstants.RULE_POSIX)) {
special = new PosixRule(this);
} else if (keyword.equals(MakeFileConstants.RULE_PRECIOUS)) {
special = new PreciousRule(this, reqs);
} else if (keyword.equals(MakeFileConstants.RULE_SILENT)) {
special = new SilentRule(this, reqs);
} else if (keyword.equals(MakeFileConstants.RULE_SUFFIXES)) {
special = new SuffixesRule(this, reqs);
} else if (keyword.equals(MakeFileConstants.RULE_DEFAULT)) {
special = new DefaultRule(this, new Command[0]);
} else if (keyword.equals(MakeFileConstants.RULE_SCCS_GET)) {
special = new SccsGetRule(this, new Command[0]);
} else if (keyword.equals(GNUMakefileConstants.RULE_PHONY)) {
special = new PhonyRule(this, reqs);
} else if (keyword.equals(GNUMakefileConstants.RULE_INTERMEDIATE)) {
special = new IntermediateRule(this, reqs);
} else if (keyword.equals(GNUMakefileConstants.RULE_SECONDARY)) {
special = new SecondaryRule(this, reqs);
} else if (keyword.equals(GNUMakefileConstants.RULE_DELETE_ON_ERROR)) {
special = new DeleteOnErrorRule(this, reqs);
} else if (keyword.equals(GNUMakefileConstants.RULE_LOW_RESOLUTION_TIME)) {
special = new LowResolutionTimeRule(this, reqs);
} else if (keyword.equals(GNUMakefileConstants.RULE_EXPORT_ALL_VARIABLES)) {
special = new ExportAllVariablesRule(this, reqs);
} else if (keyword.equals(GNUMakefileConstants.RULE_NOT_PARALLEL)) {
special = new NotParallelRule(this, reqs);
}
return special;
}
/**
* if CONDITIONAL
*
* @param line
* @return
*/
protected If parseIf(String line, Rule[] rules) {
line = line.trim();
String keyword = null;
// Move pass the keyword
for (int i = 0; i < line.length(); i++) {
if (Util.isSpace(line.charAt(i))) {
keyword = line.substring(0, i);
line = line.substring(i).trim();
break;
}
}
if (keyword == null) {
keyword = line;
}
if (keyword.equals(GNUMakefileConstants.CONDITIONAL_IF) ||
keyword.equals(GNUMakefileConstants.AT_CONDITIONAL_IF)) {
return new If(this, rules, line);
}
return null;
}
/**
*
* ifdef CONDITIONAL
* ifeq CONDITIONAL
* ifneq CONDITIONAL
* else
*
* @param line
* @return
*/
protected Conditional parseConditional(String line) {
Conditional condition = null;
line = line.trim();
String keyword = null;
// Move pass the keyword
for (int i = 0; i < line.length(); i++) {
if (Util.isSpace(line.charAt(i))) {
keyword = line.substring(0, i);
line = line.substring(i).trim();
break;
}
}
if (keyword == null) {
keyword = line;
}
if (keyword.equals(GNUMakefileConstants.CONDITIONAL_IFDEF)) {
condition = new Ifdef(this, line);
} else if (keyword.equals(GNUMakefileConstants.CONDITIONAL_IFNDEF)) {
condition = new Ifndef(this, line);
} else if (keyword.equals(GNUMakefileConstants.CONDITIONAL_IFEQ)) {
condition = new Ifeq(this, line);
} else if (keyword.equals(GNUMakefileConstants.CONDITIONAL_IFNEQ)) {
condition = new Ifneq(this, line);
} else if (keyword.equals(GNUMakefileConstants.CONDITIONAL_ELSE)) {
condition = new Else(this);
}
return condition;
}
/**
* An Autoconf directive of the form @xxx@
*/
protected AutomakeConfigMacro parseConfigMacro(String line) {
String extraChars = "_-";
char[] ch = line.toCharArray();
int i = 1;
while (Character.isLetterOrDigit(ch[i]) ||
extraChars.indexOf(ch[i]) >= 0) {
++i;
}
if (i > 1 && ch[i] == '@')
return new AutomakeConfigMacro(this, line.substring(0, i+1));
return null;
}
/**
* Format of the include directive:
* include filename1 filename2 ...
*/
protected Include parseInclude(String line) {
String[] filenames;
StringTokenizer st = new StringTokenizer(line);
int count = st.countTokens();
if (count > 0) {
filenames = new String[count - 1];
for (int i = 0; i < count; i++) {
if (i == 0) {
st.nextToken();
// ignore the "include" keyword.
continue;
}
filenames[i - 1] = st.nextToken();
}
} else {
filenames = new String[0];
}
return new Include(this, filenames, getIncludeDirectories());
}
/**
* There are three forms of the "vpath" directive:
* "vpath PATTERN DIRECTORIES"
* Specify the search path DIRECTORIES for file names that match PATTERN.
*
* The search path, DIRECTORIES, is a list of directories to be
* searched, separated by colons (semi-colons on MS-DOS and
* MS-Windows) or blanks, just like the search path used in the `VPATH' variable.
*
* "vpath PATTERN"
* Clear out the search path associated with PATTERN.
*
* "vpath"
* Clear all search paths previously specified with `vpath' directives.
*/
protected VPath parseVPath(String line) {
String pattern = null;
String[] directories;
StringTokenizer st = new StringTokenizer(line);
int count = st.countTokens();
List<String> dirs = new ArrayList<>(count);
if (count > 0) {
for (int i = 0; i < count; i++) {
if (count == 0) {
// ignore the "vpath" directive
st.nextToken();
} else if (count == 1) {
pattern = st.nextToken();
} else if (count == 3) {
String delim = " \t\n\r\f" + GNUAutomakefile.PATH_SEPARATOR; //$NON-NLS-1$
dirs.add(st.nextToken(delim));
} else {
dirs.add(st.nextToken());
}
}
}
directories = dirs.toArray(new String[0]);
if (pattern == null) {
pattern = "";
}
return new VPath(this, pattern, directories);
}
/**
* @param line
* @return
*/
protected UnExport parseUnExport(String line) {
// Pass over "unexport"
for (int i = 0; i < line.length(); i++) {
if (Util.isSpace(line.charAt(i))) {
line = line.substring(i).trim();
break;
}
}
return new UnExport(this, line);
}
protected GNUTargetRule[] parseGNUTargetRules(String line) {
String[] targetNames;
String[] normalReqs;
String[] orderReqs;
String cmd = null;
boolean doubleColon = false;
int index = Util.indexOf(line, ':');
if (index != -1) {
// Break the targets
String target = line.substring(0, index);
targetNames = PosixMakefileUtil.findTargets(target.trim());
// Some TargetRule have "::" for separator
String req = line.substring(index + 1);
doubleColon = req.startsWith(":"); //$NON-NLS-1$
if (doubleColon) {
// move pass the second ':'
req = req.substring(1);
}
// Check for command
int semicolon = Util.indexOf(req, ';');
if (semicolon != -1) {
cmd = req.substring(semicolon + 1);
req = req.substring(0, semicolon);
}
// Check for Normal and order prerequisites
String normalReq = null;
String orderReq = null;
int pipe = Util.indexOf(req, '|');
if (pipe != -1) {
normalReq = req.substring(0, pipe);
orderReq = req.substring(pipe + 1);
} else {
normalReq = req;
orderReq = ""; //$NON-NLS-1$
}
normalReqs = PosixMakefileUtil.findPrerequisites(normalReq.trim());
orderReqs = PosixMakefileUtil.findPrerequisites(orderReq.trim());
} else {
targetNames = PosixMakefileUtil.findTargets(line);
normalReqs = new String[0];
orderReqs = new String[0];
}
GNUTargetRule[] rules = new GNUTargetRule[targetNames.length];
for (int i = 0; i < targetNames.length; i++) {
rules[i] = new GNUTargetRule(this, new Target(targetNames[i]), doubleColon, normalReqs, orderReqs, new Command[0]);
if (cmd != null) {
rules[i].addDirective(new Command(this, cmd));
}
}
return rules;
}
protected GNUVariableDef parseGNUVariableDef(String line) {
line = line.trim();
GNUVariableDef vd;
// the default type.
int type = GNUVariableDef.TYPE_RECURSIVE_EXPAND;
boolean isDefine = false;
boolean isOverride = false;
boolean isTargetVariable = false;
boolean isExport = false;
String targetName = ""; //$NON-NLS-1$
String name;
StringBuffer value = new StringBuffer();
// Check for Target: Variable-assignment
isTargetVariable = GNUMakefileUtil.isTargetVariable(line);
if (isTargetVariable) {
// move to the first ':'
int colon = Util.indexOf(line, ':');
if (colon != -1) {
targetName = line.substring(0, colon).trim();
line = line.substring(colon + 1).trim();
} else {
targetName = ""; //$NON-NLS-1$
}
}
// Check for Override condition.
if (GNUMakefileUtil.isOverride(line)) {
isOverride = true;
// Move pass the keyword override.
for (int i = 0; i < line.length(); i++) {
if (Util.isSpace(line.charAt(i))) {
line = line.substring(i).trim();
break;
}
}
}
// Check for "define"
if (GNUMakefileUtil.isDefine(line)) {
isDefine = true;
// Move pass the keyword define.
for (int i = 0; i < line.length(); i++) {
if (Util.isSpace(line.charAt(i))) {
line = line.substring(i).trim();
break;
}
}
}
// Check for Override condition.
if (GNUMakefileUtil.isExport(line)) {
isExport = true;
// Move pass the keyword export.
for (int i = 0; i < line.length(); i++) {
if (Util.isSpace(line.charAt(i))) {
line = line.substring(i).trim();
break;
}
}
}
// Check for Target-variable
int index = line.indexOf('=');
if (index != -1) {
int separator = index;
// Check for "+=", ":=", "?="
if (index > 0) {
type = line.charAt(index - 1);
if (type == GNUVariableDef.TYPE_SIMPLE_EXPAND
|| type == GNUVariableDef.TYPE_APPEND
|| type == GNUVariableDef.TYPE_CONDITIONAL) {
separator = index - 1;
} else {
type = GNUVariableDef.TYPE_RECURSIVE_EXPAND;
}
}
name = line.substring(0, separator).trim();
value.append(line.substring(index + 1).trim());
} else {
name = line;
}
if (isTargetVariable) {
vd = new TargetVariable(this, targetName, name, value, isOverride, type);
} else if (isOverride && isDefine) {
vd = new OverrideDefine(this, name, value);
} else if (isDefine) {
vd = new DefineVariable(this, name, value);
} else if (isOverride) {
vd = new OverrideVariable(this, name, value, type);
} else if (isExport) {
vd = new ExportVariable(this, name, value, type);
} else {
vd = new GNUVariableDef(this, name, value, type);
}
return vd;
}
protected StaticTargetRule[] parseStaticTargetRule(String line) {
// first colon: the Targets
String targetPattern;
String[] prereqPatterns;
String[] targets;
int colon = Util.indexOf(line, ':');
if (colon > 1) {
String targetLine = line.substring(0, colon).trim();
targets = PosixMakefileUtil.findTargets(targetLine);
// second colon: Target-Pattern
line = line.substring(colon + 1);
colon = Util.indexOf(line, ':');
if (colon != -1) {
targetPattern = line.substring(0, colon).trim();
line = line.substring(colon + 1);
StringTokenizer st = new StringTokenizer(line);
int count = st.countTokens();
prereqPatterns = new String[count];
for (int i = 0; i < count; i++) {
prereqPatterns[i] = st.nextToken();
}
} else {
targetPattern = ""; //$NON-NLS-1$
prereqPatterns = new String[0];
}
} else {
targets = new String[0];
targetPattern = ""; //$NON-NLS-1$
prereqPatterns = new String[0];
}
StaticTargetRule[] staticRules = new StaticTargetRule[targets.length];
for (int i = 0; i < targets.length; i++) {
staticRules[i] = new StaticTargetRule(this, new Target(targets[i]), targetPattern, prereqPatterns, new Command[0]);
}
return staticRules;
}
/**
* @param line
* @return
*/
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));
}
@Override
public IDirective[] getDirectives(boolean expand) {
if (!expand) {
return getDirectives();
}
IDirective[] dirs = getDirectives();
ArrayList<IDirective> list = new ArrayList<>(Arrays.asList(dirs));
for (int i = 0; i < dirs.length; ++i) {
if (dirs[i] instanceof Include) {
Include include = (Include)dirs[i];
IDirective[] includedMakefiles = include.getDirectives();
for (int j = 0; j < includedMakefiles.length; ++j) {
IMakefile includedMakefile = (IMakefile)includedMakefiles[j];
list.addAll(Arrays.asList(includedMakefile.getDirectives()));
}
}
}
return list.toArray(new IDirective[list.size()]);
}
@Override
public IDirective[] getBuiltins() {
if (builtins == null) {
String location = "builtin" + File.separator + "gnu.mk"; //$NON-NLS-1$ //$NON-NLS-2$
try {
InputStream stream = FileLocator.openStream(MakeCorePlugin.getDefault().getBundle(), new Path(location), false);
GNUAutomakefile gnu = new GNUAutomakefile();
URL url = FileLocator.find(MakeCorePlugin.getDefault().getBundle(), new Path(location), null);
url = FileLocator.resolve(url);
location = url.getFile();
gnu.parse(location, 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.clone();
}
@Override
public void setIncludeDirectories(String[] dirs) {
includeDirectories = dirs.clone();
}
@Override
public String[] getIncludeDirectories() {
return includeDirectories.clone();
}
/**
* Create an IMakefile using the given IMakefileReaderProvider to fetch
* contents by name.
*
* @param fileURI URI of main file
* @param makefileReaderProvider may be <code>null</code> for EFS IFileStore reading
*/
public static IMakefile createMakefile(IFile file) throws CoreException {
URI fileURI = file.getLocationURI();
IMakefile makefile = null;
GNUAutomakefile gnu = new GNUAutomakefile();
ArrayList<String> includeList = new ArrayList<>();
includeList.add(new Path(fileURI.getPath()).removeLastSegments(1).toString());
includeList.addAll(Arrays.asList(gnu.getIncludeDirectories()));
String[] includes = includeList.toArray(new String[includeList.size()]);
gnu.setIncludeDirectories(includes);
try {
final IFileStore store = EFS.getStore(fileURI);
final IFileInfo info = store.fetchInfo();
if (!info.exists() || info.isDirectory())
throw new IOException();
MakefileReader reader = new MakefileReader(new InputStreamReader(
store.openInputStream(EFS.NONE, null)));
gnu.parse(fileURI, reader);
} catch (IOException e) {
AutotoolsUIPlugin.log(e);
Status status = new Status(IStatus.ERROR, AutotoolsUIPlugin.PLUGIN_ID, e.getLocalizedMessage());
throw new CoreException(status);
}
makefile = gnu;
return makefile;
}
public static void main(String[] args) {
try {
String filename = "Makefile"; //$NON-NLS-1$
if (args.length == 1) {
filename = args[0];
}
GNUAutomakefile makefile = new GNUAutomakefile();
makefile.parse(filename);
IDirective[] directive = makefile.getDirectives();
for (int i = 0; i < directive.length; i++) {
//System.out.println("Rule[" + i +"]");
System.out.print(directive[i]);
}
} catch (IOException e) {
System.out.println(e);
}
}
@Override
public IBuiltinFunction[] getBuiltinFunctions() {
return new IBuiltinFunction[0];
}
@Override
public IAutomaticVariable[] getAutomaticVariables() {
return new IAutomaticVariable[0];
}
}