// NAnt - A .NET build tool
// Copyright (C) 2001-2002 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)
// Anthony LoveFrancisco (ants@fu.org)
// Hani Atassi (haniatassi@users.sourceforge.net)
//
// TODO: review interface for future compatibility/customizations issues
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.Core.Util;
using NAnt.VisualCpp.Types;
using NAnt.VisualCpp.Util;
namespace NAnt.VisualCpp.Tasks {
///
/// Compiles C/C++ programs using cl.exe, Microsoft's C/C++ compiler.
///
///
/// This task is intended for version 13.00.9466 of cl.exe.
///
///
/// Compiles helloworld.cpp for the Common Language Runtime.
///
///
///
///
///
///
/// ]]>
///
///
[TaskName("cl")]
public class ClTask : ExternalProgramBase {
#region Private Instance Fields
private string _responseFileName;
private DirectoryInfo _outputDir;
private string _pchFile;
private PrecompiledHeaderMode _precompileHeaderMode = PrecompiledHeaderMode.Use;
private string _pchThroughFile;
private FileSet _sources = new FileSet();
private FileSet _includeDirs = new FileSet();
private FileSet _metaDataIncludeDirs = new FileSet();
private FileSet _forcedUsingFiles = new FileSet();
private bool _managedExtensions;
private CharacterSet _characterSet = CharacterSet.NotSet;
private string _options;
private OptionCollection _defines = new OptionCollection();
private OptionCollection _undefines = new OptionCollection();
private string _objectFile;
private string _pdbFile;
private Hashtable _resolvedIncludes;
private Regex _includeRegex;
private StringCollection _dirtySources = new StringCollection();
#endregion Private Instance Fields
#region Public Instance Constructors
///
/// Initializes a new instance of the class.
///
public ClTask() {
_resolvedIncludes = CollectionsUtil.CreateCaseInsensitiveHashtable();
_includeRegex = new Regex("^[\\s]*#include[\\s]*[\"<](?'includefile'[^\">]+)[\">][\\S\\s]*$");
}
#endregion Public Instance Constructors
#region Public Instance Properties
///
/// Directory where all output files are placed.
///
[TaskAttribute("outputdir", Required=true)]
public DirectoryInfo OutputDir {
get { return _outputDir; }
set { _outputDir = value; }
}
///
/// Specifies the path and/or name of the generated precompiled header
/// file - given either relative to or as an
/// absolute path.
///
[TaskAttribute("pchfile")]
public string PchFile {
get { return (_pchFile != null) ? Path.Combine(OutputDir.FullName, _pchFile) : null; }
set { _pchFile = StringUtils.ConvertEmptyToNull(value); }
}
///
/// The path of the boundary file when generating/using the
/// specified . If a precompiled header file is
/// not specified then this attribute is ignored.
///
[TaskAttribute("pchthroughfile")]
public string PchThroughFile {
get { return _pchThroughFile; }
set { _pchThroughFile = StringUtils.ConvertEmptyToNull(value); }
}
///
/// The mode in which the specified (if any) is
/// used. The default is .
///
[TaskAttribute("pchmode")]
public PrecompiledHeaderMode PchMode {
get { return _precompileHeaderMode; }
set {
if (!Enum.IsDefined(typeof(PrecompiledHeaderMode), value)) {
throw new ArgumentException(string.Format(
CultureInfo.InvariantCulture,
"An invalid type {0} was specified.", value));
} else {
_precompileHeaderMode = value;
}
}
}
///
/// Specifies whether Managed Extensions for C++ should be enabled.
/// The default is .
///
[TaskAttribute("managedextensions")]
[BooleanValidator()]
public bool ManagedExtensions {
get { return _managedExtensions; }
set { _managedExtensions = value;}
}
///
/// Tells the compiler to use the specified character set.
///
[TaskAttribute("characterset", Required=false)]
public CharacterSet CharacterSet {
get { return _characterSet; }
set {
if (!Enum.IsDefined(typeof(CharacterSet), value)) {
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
"An invalid character set '{0}' was specified.", value));
} else {
this._characterSet = value;
}
}
}
///
/// Options to pass to the compiler.
///
[TaskAttribute("options")]
public string Options {
get { return _options; }
set { _options = value; }
}
///
/// The list of files to compile.
///
[BuildElement("sources")]
public FileSet Sources {
get { return _sources; }
set { _sources = value; }
}
///
/// The list of directories in which to search for include files.
///
[BuildElement("includedirs")]
public FileSet IncludeDirs {
get { return _includeDirs; }
set { _includeDirs = value; }
}
///
/// Directories that the compiler will search to resolve file references
/// passed to the #using directive.
///
[BuildElement("metadataincludedirs")]
public FileSet MetaDataIncludeDirs {
get { return _metaDataIncludeDirs; }
set { _metaDataIncludeDirs = value; }
}
///
/// Specifies metadata files to reference in this compilation as an
/// alternative to passing a file name to #using in source code.
///
[BuildElement("forcedusingfiles")]
public FileSet ForcedUsingFiles {
get { return _forcedUsingFiles; }
set { _forcedUsingFiles = value; }
}
///
/// Macro definitions to pass to cl.exe.
/// Each entry will generate a /D
///
[BuildElementCollection("defines", "define")]
public OptionCollection Defines {
get { return _defines; }
}
///
/// Macro undefines (/U) to pass to cl.exe.
///
[BuildElementCollection("undefines", "undefine")]
public OptionCollection Undefines {
get { return _undefines; }
}
///
/// A name to override the default object file name; can be either a file
/// or directory name. The default is the output directory.
///
[TaskAttribute("objectfile")]
public string ObjectFile {
get { return (_objectFile != null ? Path.Combine(OutputDir.FullName, _objectFile) : OutputDir.FullName + "/"); }
set { _objectFile = StringUtils.ConvertEmptyToNull(value); }
}
///
/// A name for the compiler-generated PDB file; can be either a file or
/// directory name. The default is the output directory.
///
[TaskAttribute("pdbfile")]
public string ProgramDatabaseFile {
get { return (_pdbFile != null ? Path.Combine(OutputDir.FullName, _pdbFile) : OutputDir.FullName + "/"); }
set { _pdbFile = StringUtils.ConvertEmptyToNull(value); }
}
#endregion Public Instance Properties
#region Override implementation of ExternalProgramBase
///
/// Gets the filename of the external program to start.
///
/// The filename of the external program.
public override string ProgramFileName {get {return Name;}}
///
/// Gets the command-line arguments for the external program.
///
///
/// The command-line arguments for the external program.
///
public override string ProgramArguments {
get { return "@" + "\"" + _responseFileName + "\""; }
}
#endregion Override implementation of ExternalProgramBase
#region Override implementation of Task
///
/// Compiles the sources.
///
protected override void ExecuteTask() {
// ensure base directory is set, even if fileset was not initialized
// from XML
if (Sources.BaseDirectory == null) {
Sources.BaseDirectory = new DirectoryInfo(Project.BaseDirectory);
}
if (IncludeDirs.BaseDirectory == null) {
IncludeDirs.BaseDirectory = new DirectoryInfo(Project.BaseDirectory);
}
if (MetaDataIncludeDirs.BaseDirectory == null) {
MetaDataIncludeDirs.BaseDirectory = new DirectoryInfo(Project.BaseDirectory);
}
if (ForcedUsingFiles.BaseDirectory == null) {
ForcedUsingFiles.BaseDirectory = new DirectoryInfo(Project.BaseDirectory);
}
if (NeedsCompiling()) {
Log(Level.Info, "Compiling {0} files to '{1}'.",
_dirtySources.Count, OutputDir.FullName);
// create temp response file to hold compiler options
_responseFileName = Path.GetTempFileName();
StreamWriter writer = new StreamWriter(_responseFileName);
try {
// write basic switches
writer.WriteLine("/c"); // compile only
if (Options != null) {
// write user defined options
writer.WriteLine(Options);
}
if (ManagedExtensions) {
// enables Managed Extensions for C++
writer.WriteLine("/clr");
}
// write preprocesser define(s)
foreach (Option define in Defines) {
if (!define.IfDefined || define.UnlessDefined) {
continue;
}
if (define.Value == null) {
writer.WriteLine("/D " + QuoteArgumentValue(define.OptionName));
} else {
writer.WriteLine("/D " + QuoteArgumentValue(define.OptionName
+ "=" + ArgumentUtils.DuplicateTrailingBackslash(define.Value)));
}
}
// write preprocesser undefine(s)
foreach (Option undefine in Undefines) {
if (!undefine.IfDefined || undefine.UnlessDefined) {
continue;
}
writer.WriteLine("/U " + QuoteArgumentValue(undefine.OptionName));
}
// write user provided include directories
foreach (string include in IncludeDirs.DirectoryNames) {
writer.WriteLine("/I {0}", QuoteArgumentValue(include));
}
// write directories that the compiler will search to resolve
// file references passed to the #using directive
foreach (string metaDataIncludeDir in MetaDataIncludeDirs.DirectoryNames) {
writer.WriteLine("/AI {0}", QuoteArgumentValue(metaDataIncludeDir));
}
// writes metadata files to reference in this compilation
// as an alternative to passing a file name to #using in
// source code
foreach (string forcedUsingFile in ForcedUsingFiles.FileNames) {
writer.WriteLine("/FU {0}", QuoteArgumentValue(forcedUsingFile));
}
// program database file
writer.WriteLine("/Fd{0}", QuoteArgumentValue(ProgramDatabaseFile));
// the object file or output directory
writer.WriteLine("/Fo{0}", QuoteArgumentValue(ObjectFile));
// specify pch file, if user specified one
if (PchFile != null) {
writer.WriteLine("/Fp{0}", QuoteArgumentValue(PchFile));
switch (PchMode) {
case PrecompiledHeaderMode.Use:
writer.Write("/Yu");
break;
case PrecompiledHeaderMode.Create:
writer.Write("/Yc");
break;
case PrecompiledHeaderMode.AutoCreate:
writer.Write("/YX");
break;
}
if (PchThroughFile != null) {
writer.WriteLine("{0}", QuoteArgumentValue(PchThroughFile));
}
}
// write each of the filenames
foreach (string filename in _dirtySources) {
writer.WriteLine(QuoteArgumentValue(filename));
}
// tell compiler which character set to use
switch (CharacterSet) {
case CharacterSet.Unicode:
writer.WriteLine("/D \"_UNICODE\"");
writer.WriteLine("/D \"UNICODE\"");
break;
case CharacterSet.MultiByte:
writer.WriteLine("/D \"_MBCS\"");
break;
}
writer.Close();
if (Verbose) {
// display response file contents
Log(Level.Info, "Contents of {0}.", _responseFileName);
StreamReader reader = File.OpenText(_responseFileName);
Log(Level.Info, reader.ReadToEnd());
reader.Close();
}
// suppresses display of the sign-on banner
// (this has no effect in response file)
this.Arguments.Add(new Argument("/nologo"));
// call base class to do the actual work
base.ExecuteTask();
} finally {
// make sure we delete response file even if an exception is thrown
writer.Close(); // make sure stream is closed or file cannot be deleted
File.Delete(_responseFileName);
_responseFileName = null;
}
}
}
#endregion Override implementation of Task
#region Protected Instance Methods
///
/// Determines if the sources need to be compiled.
///
protected virtual bool NeedsCompiling() {
if (!IsPchfileUpToDate()) {
// if pch file is not up-to-date, then mark all sources as dirty
Log(Level.Verbose, "PCH out of date, recompiling all sources.");
_dirtySources = StringUtils.Clone(Sources.FileNames);
return true;
}
return !AreObjsUpToDate();
}
#endregion Protected Instance Methods
#region Private Instance Methods
///
/// Determines whether the precompiled header file is up-to-date.
///
///
/// if no precompiled header file was specified;
/// otherwise, .
///
///
/// In order to determine accurately whether the precompile header file
/// is up-to-date, we'd need scan all the header files that are pulled
/// in. As this is not implemented right now, its safer to always
/// recompile.
///
private bool IsPchfileUpToDate() {
// TODO: also check for updated metadata includes (#using directives)
// if no pch file, then then it is theoretically up to date
if (PchFile == null) {
return true;
}
// if pch file declared, but doesn't exist, it must be stale
FileInfo pchFileInfo = new FileInfo(PchFile);
if (!pchFileInfo.Exists) {
Log(Level.Verbose, "'{0}' does not exist, recompiling.",
pchFileInfo.FullName);
return false;
}
// if an existing pch file is used, then pch file must never be
// recompiled
if (PchMode == PrecompiledHeaderMode.Use) {
return true;
}
// check if sources fresher than pch file
string fileName = FileSet.FindMoreRecentLastWriteTime(Sources.FileNames, pchFileInfo.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' is newer than pch file, recompiling.",
fileName);
return false;
}
// check if assembly references are fresher than pch file
fileName = FileSet.FindMoreRecentLastWriteTime(ForcedUsingFiles.FileNames, pchFileInfo.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' is newer than pch file, recompiling.",
fileName);
return false;
}
// check if included headers in source are modified after pch was compiled
foreach (string sourceFile in Sources.FileNames) {
fileName = FindUpdatedInclude(sourceFile, pchFileInfo.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return false;
}
}
return true;
}
private bool IsObjUpToDate(string srcFileName) {
// TODO: also check for updated metadata includes (#using directives)
FileInfo objFileInfo = new FileInfo(GetObjOutputFile(srcFileName,
ObjectFile));
// if obj file doesn't exist, it must be stale
if (!objFileInfo.Exists) {
Log(Level.Verbose, "'{0}' does not exist, recompiling.",
objFileInfo.FullName);
return false;
}
// if obj file is older than the source file, it is stale
string fileName = FileSet.FindMoreRecentLastWriteTime(srcFileName, objFileInfo.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return false;
}
// check if assembly references are fresher than pch file
fileName = FileSet.FindMoreRecentLastWriteTime(ForcedUsingFiles.FileNames, objFileInfo.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return false;
}
// check if included headers in source are modified after obj was compiled
fileName = FindUpdatedInclude(srcFileName, objFileInfo.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return false;
}
if (PchFile != null && PchMode == PrecompiledHeaderMode.Use) {
// check if precompiled header is modified after obj was compiled
fileName = FileSet.FindMoreRecentLastWriteTime(PchFile, objFileInfo.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return false;
}
}
return true;
}
private bool AreObjsUpToDate() {
foreach (string filename in Sources.FileNames) {
// if the source file does not exist, then we'll consider it
// not up-to-date
if (!File.Exists(filename)) {
Log(Level.Verbose, "'{0}' does not exist, recompiling.",
filename);
_dirtySources.Add(filename);
continue;
}
if (!IsObjUpToDate(filename)) {
_dirtySources.Add(filename);
continue;
}
}
// if there are no outdated files, then the OBJs are up to date
return _dirtySources.Count == 0;
}
///
/// Determines whether any file that are includes in the specified
/// source file has been updated after the obj was compiled.
///
/// The source file to check.
/// The last write time of the compiled obj.
///
/// The full path to the include file that was modified after the obj
/// was compiled, or if no include files were
/// modified since the obj was compiled.
///
///
///
/// To determine what includes are defined in a source file, conditional
/// directives are not honored.
///
///
/// If a given include cannot be resolved to an existing file, then
/// it will be considered stable.
///
///
private string FindUpdatedInclude(string srcFileName, DateTime objLastWriteTime) {
// quick and dirty code to check whether includes have been modified
// after the source was last modified
// TODO: recursively check includes
Log(Level.Debug, "Checking whether includes of \"{0}\" have been"
+ " updated.", srcFileName);
// holds the line we're parsing
string line;
// locate include directives in source file
using (StreamReader sr = new StreamReader(srcFileName, true)) {
while ((line = sr.ReadLine()) != null) {
Match match = _includeRegex.Match(line);
if (match.Groups.Count != 2) {
continue;
}
string includeFile = match.Groups["includefile"].Value;
Log(Level.Debug, "Checking include \"{0}\"...", includeFile);
string resolvedInclude = _resolvedIncludes[includeFile] as string;
if (resolvedInclude == null) {
foreach (string includeDir in IncludeDirs.DirectoryNames) {
string foundIncludeFile = FileUtils.CombinePaths(includeDir, includeFile);
if (File.Exists(foundIncludeFile)) {
Log(Level.Debug, "Found include \"{0}\" in"
+ " includedirs.", includeFile);
resolvedInclude = foundIncludeFile;
break;
}
}
// if we could not locate include in include dirs and
// source dir, then try to locate include in INCLUDE
// env var
if (resolvedInclude == null) {
PathScanner pathScanner = new PathScanner();
pathScanner.Add(includeFile);
StringCollection includes = pathScanner.Scan("INCLUDE");
if (includes.Count > 0) {
Log(Level.Debug, "Found include \"{0}\" in"
+ " INCLUDE.", includeFile);
resolvedInclude = includes[0];
}
}
// if we could not locate include in include dirs
// and INCLUDE env var then check for include in base
// directory (which is used as working dir)
if (resolvedInclude == null) {
string foundIncludeFile = FileUtils.CombinePaths(
BaseDirectory.FullName, includeFile);
if (File.Exists(foundIncludeFile)) {
Log(Level.Debug, "Found include \"{0}\" in"
+ " working directory.", includeFile);
resolvedInclude = foundIncludeFile;
}
}
if (resolvedInclude != null) {
_resolvedIncludes.Add(includeFile, resolvedInclude);
}
}
if (resolvedInclude != null) {
if (File.GetLastWriteTime(resolvedInclude) > objLastWriteTime) {
return resolvedInclude;
}
} else {
// TODO: what do we do if the include cannot be located ?
//
// for now we'll consider the obj file to be up-to-date
Log(Level.Debug, "Include \"{0}\" could not be located.",
includeFile);
}
}
}
return null;
}
#endregion Private Instance Methods
#region Public Static Methods
///
/// Quotes an argument value and duplicates trailing backslahes.
///
/// The argument value to quote.
///
/// The quotes argument value.
///
public static string QuoteArgumentValue(string value) {
return ArgumentUtils.QuoteArgumentValue(value,
BackslashProcessingMethod.Duplicate);
}
///
/// Determines the file name of the OBJ file for the specified source
/// file.
///
/// The source file for which the OBJ file should be determined.
/// The path of the object file.
///
/// The file name of the OBJ file for the specified source file.
///
public static string GetObjOutputFile(string srcFile, string objectPath) {
if (srcFile == null) {
throw new ArgumentNullException("srcFile");
}
if (objectPath == null) {
throw new ArgumentNullException("objectPath");
}
// check if objectPath is file
if (!objectPath.EndsWith("/") && Path.GetFileName(objectPath).Length != 0) {
// if object file has no extension then use .obj, otherwise
// use objectPath as is
if (Path.GetExtension(objectPath).Length == 0) {
return Path.ChangeExtension(objectPath, ".obj");
} else {
return objectPath;
}
} else {
return FileUtils.CombinePaths(objectPath,
Path.GetFileNameWithoutExtension(srcFile) + ".obj");
}
}
#endregion Public Static Methods
///
/// Defines the supported modes for the use of precompiled header files.
///
public enum PrecompiledHeaderMode : int {
///
/// Create a precompiled header file.
///
///
/// For further information on the use of this option
/// see the Microsoft documentation on the C++ compiler flag /Yc.
///
Create = 1,
///
/// Automatically create a precompiled header file if necessary.
///
///
/// For further information on the use of this option
/// see the Microsoft documentation on the C++ compiler flag /YX.
///
AutoCreate = 2,
///
/// Use a (previously generated) precompiled header file.
///
///
/// For further information on the use of this option
/// see the Microsoft documentation on the C++ compiler flag /Yu.
///
Use = 0
}
}
}
#if unused
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.00.9466 for 80x86
Copyright (C) Microsoft Corporation 1984-2001. All rights reserved.
C/C++ COMPILER OPTIONS
-OPTIMIZATION-
/O1 minimize space /Op[-] improve floating-pt consistency
/O2 maximize speed /Os favor code space
/Oa assume no aliasing /Ot favor code speed
/Ob inline expansion (default n=0) /Ow assume cross-function aliasing
/Od disable optimizations (default) /Ox maximum opts. (/Ogityb2 /Gs)
/Og enable global optimization /Oy[-] enable frame pointer omission
/Oi enable intrinsic functions
-CODE GENERATION-
/G3 optimize for 80386 /GH enable _pexit function call
/G4 optimize for 80486 /GR[-] enable C++ RTTI
/G5 optimize for Pentium /GX[-] enable C++ EH (same as /EHsc)
/G6 optimize for PPro, P-II, P-III /EHs enable C++ EH (no SEH exceptions)
/GB optimize for blended model (default) /EHa enable C++ EH (w/ SEH exceptions)
/Gd __cdecl calling convention /EHc extern "C" defaults to nothrow
/Gr __fastcall calling convention /GT generate fiber-safe TLS accesses
/Gz __stdcall calling convention /Gm[-] enable minimal rebuild
/GA optimize for Windows Application /GL[-] enable link-time code generation
/Gf enable string pooling /QIfdiv[-] enable Pentium FDIV fix
/GF enable read-only string pooling /QI0f[-] enable Pentium 0x0f fix
/Gy separate functions for linker /QIfist[-] use FIST instead of ftol()
/GZ Enable stack checks (/RTCs) /RTC1 Enable fast checks (/RTCsu)
/Ge force stack checking for all funcs /RTCc Convert to smaller type checks
/Gs[num] control stack checking calls /RTCs Stack Frame runtime checking
/GS enable security checks /RTCu Uninitialized local usage checks
/Gh enable _penter function call
/clr[:noAssembly] compile for the common language runtime
noAssembly - do not produce an assembly
-OUTPUT FILES-
/Fa[file] name assembly listing file /Fo name object file
/FA[sc] configure assembly listing /Fp name precompiled header file
/Fd[file] name .PDB file /Fr[file] name source browser file
/Fe name executable file /FR[file] name extended .SBR file
/Fm[file] name map file
-PREPROCESSOR-
/AI add to assembly search path /Fx merge injected code to file
/FU forced using assembly/module /FI name forced include file
/C don't strip comments /U remove predefined macro
/D{=|#} define macro /u remove all predefined macros
/E preprocess to stdout /I add to include search path
/EP preprocess to stdout, no #line /X ignore "standard places"
/P preprocess to file
-LANGUAGE-
/Zi enable debugging information /Zl omit default library name in .OBJ
/ZI enable Edit and Continue debug info /Zg generate function prototypes
/Z7 enable old-style debug info /Zs syntax check only
/Zd line number debugging info only /vd{0|1} disable/enable vtordisp
/Zp[n] pack structs on n-byte boundary /vm type of pointers to members
/Za disable extensions (implies /Op) /noBool disable "bool" keyword
/Ze enable extensions (default)
/Zc:arg1[,arg2] C++ language conformance, where arguments can be:
forScope - enforce Standard C++ for scoping rules
wchar_t - wchar_t is the native type, not a typedef
-MISCELLANEOUS-
@ options response file /wo issue warning n once
/?, /help print this help message /w set warning level 1-4 for n
/c compile only, no link /W set warning level (default n=1)
/H max external name length /Wall enable all warnings
/J default char type is unsigned /Wp64 enable 64 bit porting warnings
/nologo suppress copyright message /WX treat warnings as errors
/showIncludes show include file names /WL enable one line diagnostics
/Tc compile file as .c /Yc[file] create .PCH file
/Tp compile file as .cpp /Yd put debug info in every .OBJ
/TC compile all files as .c /Yl[sym] inject .PCH ref for debug lib
/TP compile all files as .cpp /Yu[file] use .PCH file
/V set version string /YX[file] automatic .PCH
/w disable all warnings /Y- disable all PCH options
/wd disable warning n /Zm max memory alloc (% of default)
/we treat warning n as an error
-LINKING-
/MD link with MSVCRT.LIB /MDd link with MSVCRTD.LIB debug lib
/ML link with LIBC.LIB /MLd link with LIBCD.LIB debug lib
/MT link with LIBCMT.LIB /MTd link with LIBCMTD.LIB debug lib
/LD Create .DLL /F set stack size
/LDd Create .DLL debug library /link [linker options and libraries]
#endif