// NAnt - A .NET build tool
// Copyright (C) 2001-2003 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// Shawn Van Ness (nantluver@arithex.com)
// Gerry Shaw (gerry_shaw@yahoo.com)
// Ian MacLean ( ian@maclean.ms )
// Eric V. Smith (ericsmith@windsor.com)
// Hani Atassi (haniatassi@users.sourceforge.net)
// TODO: review interface for future compatibility/customizations issues
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.VisualCpp.Util;
namespace NAnt.VisualCpp.Tasks {
///
/// Compiles resources using rc.exe, Microsoft's Win32 resource
/// compiler.
///
///
///
/// Compile text.rc to text.res using the default options.
///
///
///
/// ]]>
///
///
///
///
/// Compile text.rc, passing an additional option.
///
///
///
/// ]]>
///
///
[TaskName("rc")]
public class RcTask : ExternalProgramBase {
#region Private Instance Fields
private FileInfo _outputFile;
private string _options;
private int _langId = 0;
private FileInfo _rcFile;
private FileSet _includeDirs = new FileSet();
private OptionCollection _defines = new OptionCollection();
#endregion Private Instance Fields
#region Public Instance Properties
///
/// Options to pass to the compiler.
///
[TaskAttribute("options")]
public string Options {
get { return _options; }
set { _options = value; }
}
///
/// Output file.
///
[TaskAttribute("output")]
public FileInfo OutputFile {
get {
if (_outputFile == null) {
_outputFile = new FileInfo(Path.ChangeExtension(RcFile.FullName, "RES"));
}
return _outputFile;
}
set { _outputFile = value; }
}
///
/// The resource file to compile.
///
[TaskAttribute("rcfile", Required=true)]
public FileInfo RcFile {
get { return _rcFile; }
set { _rcFile = value; }
}
///
/// Default language ID.
///
[TaskAttribute("langid", Required=false)]
public int LangId {
get { return _langId; }
set { _langId = value; }
}
///
/// The list of directories in which to search for include files.
///
[BuildElement("includedirs")]
public FileSet IncludeDirs {
get { return _includeDirs; }
set { _includeDirs = value; }
}
///
/// Macro definitions to pass to rc.exe.
/// Each entry will generate a /d
///
[BuildElementCollection("defines", "define")]
public OptionCollection Defines {
get { return _defines; }
}
#endregion Public Instance Properties
#region Override implementation of ExternalProgramBase
///
/// Filename of program to execute
///
public override string ProgramFileName {
get { return Name; }
}
///
/// Arguments of program to execute
///
public override string ProgramArguments {
get {
StringBuilder str = new StringBuilder();
if (Verbose) {
str.Append("/v ");
}
str.AppendFormat(CultureInfo.InvariantCulture,
"/fo\"{0}\" ", OutputFile.FullName);
if (Options != null) {
str.AppendFormat(CultureInfo.InvariantCulture,
"{0} ", Options);
}
if (LangId != 0) {
str.AppendFormat("/l 0x{0:X} ", LangId);
}
// append user provided include directories
foreach (string include in IncludeDirs.DirectoryNames) {
str.AppendFormat("/i {0} ", ArgumentUtils.QuoteArgumentValue(
include, BackslashProcessingMethod.Duplicate));
}
// append user definitions
foreach (Option define in Defines) {
if (!define.IfDefined || define.UnlessDefined) {
continue;
}
if (define.Value == null) {
str.AppendFormat("/d {0} ", ArgumentUtils.DuplicateTrailingBackslash(define.OptionName));
} else {
str.AppendFormat("/d {0}={1} ", define.OptionName, ArgumentUtils.DuplicateTrailingBackslash(define.Value));
}
}
str.AppendFormat(CultureInfo.InvariantCulture,
"\"{0}\" ", RcFile.FullName);
return str.ToString();
}
}
///
/// Compile the resource file
///
protected override void ExecuteTask() {
if (IncludeDirs.BaseDirectory == null) {
IncludeDirs.BaseDirectory = new DirectoryInfo(Project.BaseDirectory);
}
if (NeedsCompiling()) {
Log(Level.Info, "Compiling \"{0}\" to \"{1}\".", RcFile.FullName,
OutputFile.FullName);
base.ExecuteTask();
}
}
#endregion Override implementation of ExternalProgramBase
#region Protected Instance Methods
///
/// Determines if the resource need compiling.
///
protected virtual bool NeedsCompiling() {
if (!OutputFile.Exists) {
Log(Level.Verbose, "'{0}' does not exist, recompiling.",
OutputFile.FullName);
return true;
}
// if output file file is older the resource file, it is stale
string fileName = FileSet.FindMoreRecentLastWriteTime(
RcFile.FullName, OutputFile.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' is out of date, recompiling.",
fileName);
return true;
}
// if resource file does not exist, then let compiler handle error
if (!RcFile.Exists) {
return true;
}
// check whether external files have been updated
Regex regBitmap = new Regex("IDB_(?\\w+)\\s+BITMAP\\s+\\\"(?[^\\\"]+)\\\"", RegexOptions.Singleline | RegexOptions.IgnoreCase);
Regex regIcon = new Regex("IDI_(?\\w+)\\s+ICON\\s+\\\"(?[^\\\"]+)\\\"", RegexOptions.Singleline | RegexOptions.IgnoreCase);
Regex regBinary = new Regex("IDR_(?\\w+)\\s+(?\\w+)\\s+\\\"(?[^\\\"]+)\\\"", RegexOptions.Singleline | RegexOptions.IgnoreCase);
using (StreamReader sr = new StreamReader(RcFile.FullName)) {
while (sr.Peek() != -1) {
string line = sr.ReadLine();
Match resourceMatch = regBitmap.Match(line);
if (resourceMatch.Success) {
return CheckResourceTimeStamp(resourceMatch.Groups["file"].Value);
}
resourceMatch = regIcon.Match(line);
if (resourceMatch.Success) {
return CheckResourceTimeStamp(resourceMatch.Groups["file"].Value);
}
resourceMatch = regBinary.Match(line);
if (resourceMatch.Success) {
return CheckResourceTimeStamp(resourceMatch.Groups["file"].Value);
}
}
}
// output file is up-to-date
return false;
}
///
/// Check if a resource file has been updated.
///
///
///
private bool CheckResourceTimeStamp(string filePath ) {
string fileName;
string externalFile = Path.Combine(RcFile.DirectoryName,
filePath);
fileName = FileSet.FindMoreRecentLastWriteTime(
externalFile, OutputFile.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return true;
}
return false;
}
#endregion Protected Instance Methods
}
}
#if unused
Microsoft (R) Windows (R) Resource Compiler, Version 5.1.2264.1 - Build 2264
Copyright (C) Microsoft Corp. 1985-1998. All rights reserved.
Usage: rc [options] .RC input file
Switches:
/r Emit .RES file (optional)
/v Verbose (print progress messages)
/d Define a symbol
/u Undefine a symbol
/fo Rename .RES file
/l Default language ID in hex
/i Add a path for INCLUDE searches
/x Ignore INCLUDE environment variable
/c Define a code page used by NLS conversion
/w Warn on Invalid codepage in .rc (default is an error)
/y Don't warn if there are duplicate control ID's
/n Append null's to all strings in the string tables.
Flags may be either upper or lower case
#endif