-
Notifications
You must be signed in to change notification settings - Fork 176
Custom Targets
The Sharpmake.Target
class contains a premade collection of fragments that
you can use to create your set of targets, but if you need extra options to
configure your targets, it is also possible to create your own target class and
your own fragment enumerations.
In this tutorial we are going to create a fragment that allows your script to select whether C++ exceptions are enabled in a simple C++ project.
Here is the sample code we are going to work with.
// main.cpp
#include <cstdlib>
#include <cstdio>
// The compiler generates warnings if it sees exception handling code while
// exceptions are turned off, so use a macro magic hack.
#if ALLOW_THROW
# define THROW(ex, msg) throw ex(msg)
# include <stdexcept>
#else
# define THROW(...) abort()
#endif // ALLOW_THROW
int Divide(int left_operand, int right_operand)
{
if (right_operand == 0)
THROW(std::domain_error, "DIV/0!");
return left_operand / right_operand;
}
int main()
{
// Using the C I/O library instead of iostream because the C library
// does not generate warnings when C++ exceptions are disabled.
int x, y;
printf("First operand: ");
scanf("%i", &x);
printf("\nSecond operand: ");
scanf("%i", &y);
int quotient = Divide(x, y);
printf("%i / %i = %i", x, y, quotient);
return 0;
}
The ALLOW_THROW
hack is just there so that the code does not attempt to throw
if exceptions are disabled. This is just so that the compiler does not warn
that there is a throw when exceptions are disabled.
Let's start by defining our custom fragment. This will let us configure the exception settings.
// main.sharpmake.cs
using System;
using Sharpmake;
// A fragment is a bit flag enumeration with the [Fragment] attribute on it.
[Fragment, Flags]
public enum ErrorHandlingModes
{
CppExceptions = 1, // Use C++ exceptions.
Abort = 2 // Use abort().
}
A fragment enumeration is just an ordinary bit flag enumeration decorated with
the [Fragment]
attribute. The name of the elements have no semantics to
Sharpmake; it is up to the script to interpret it.
Since our fragment is not found anywhere in Sharpmake.Target
, we have no
choice to create a custom target.
// Create a custom target that allows the code to specify the error handling
// mode, on top of other common fragments we care about.
public class CustomTarget : ITarget
{
// DevEnv and Platform are mandatory on all targets so we define them.
public DevEnv VisualStudioVersion;
public Platform Platform;
// Also put debug/release configurations since this is common.
public Optimization Optimization;
// Specifies how that platform handles exceptions. This is our custom
// fragment.
public ErrorHandlingModes ErrorHandling;
//
// Put any other fragment you want to configure here.
//
}
A custom target is simply a class that implements Sharpmake.ITarget
with a
list of fragments. The fragments are simple C# fields.
// The project definition.
[Generate]
public class CustomTargetProject : Project
{
// Projects and solutions that use a custom target (ie: that is not the
// class Target) must pass the type of the target class it's going to be
// using to the base class constructor, otherwise Sharpmake will not know
// how to call the Configure methods.
public CustomTargetProject()
: base(typeof(CustomTarget))
{
Name = "CustomTarget";
SourceRootPath = @"[project.SharpmakeCsPath]";
AddTargets(new CustomTarget
{
VisualStudioVersion = DevEnv.vs2015,
Platform = Platform.win32 | Platform.win64,
Optimization = Optimization.Debug | Optimization.Release,
ErrorHandling = ErrorHandlingModes.CppExceptions | ErrorHandlingModes.Abort
});
}
// Because we are using a custom target class, the configure method accepts
// a CustomTarget instead of a Target. There is no need to type cast.
[Configure]
public void ConfigureAll(Configuration conf, CustomTarget target)
{
conf.ProjectPath = @"[project.SharpmakeCsPath]\generated";
// No Microsoft, you don't deprecate standard C library functions
// unless the ISO commitee says it's deprecated.........
conf.Defines.Add("_CRT_SECURE_NO_WARNINGS");
// Sets the compiler exception flag depending on whether throwing is
// enabled. Also define the ALLOW_THROW symbol to let the code know
// that it can throw.
if (target.ErrorHandling == ErrorHandlingModes.CppExceptions)
{
// This just let the code know that it can throw so it defines the
// right stuff. Nothing to do with Sharpmake.
conf.Defines.Add("ALLOW_THROW");
// Turns the Exception compiler option ON.
//
// Compiler options are set by adding values to the Options
// collection. Sharpmake knows which option you want to set simply
// by looking at the type of the option you added. Here, since we
// added a value of type Options::Vc::Compiler::Exceptions,
// Sharpmake understands that we are setting the exception setting.
//
// Sharpmake options for Visual C++ are sorted by their section in
// the project properties dialog box. You can use IntelliSense to
// explore the options that are available.
conf.Options.Add(Options.Vc.Compiler.Exceptions.Enable);
}
else if (target.ErrorHandling == ErrorHandlingModes.Abort)
{
conf.Options.Add(Options.Vc.Compiler.Exceptions.Disable);
}
}
}
// The solution definition.
[Generate]
public class CustomTargetSolution : Solution
{
public CustomTargetSolution()
: base(typeof(CustomTarget))
{
Name = "CustomTarget";
AddTargets(new CustomTarget
{
VisualStudioVersion = DevEnv.vs2015,
Platform = Platform.win32 | Platform.win64,
Optimization = Optimization.Debug | Optimization.Release,
ErrorHandling = ErrorHandlingModes.CppExceptions | ErrorHandlingModes.Abort
});
}
[Configure]
public void ConfigureAll(Configuration conf, CustomTarget target)
{
conf.SolutionPath = @"[solution.SharpmakeCsPath]\generated";
conf.AddProject<CustomTargetProject>(target);
}
}
public static class Main
{
[Sharpmake.Main]
public static void SharpmakeMain(Sharpmake.Arguments arguments)
{
arguments.Generate<CustomTargetSolution>();
}
}
When creating projects and solutions that use a custom target (like our
CustomTarget
) you must pass the type of your custom target type to the
base class constructor.
When using a custom target, your configure methods' second argument can be your
custom target instead of the usual Target
or ITarget
, so there is no need
to type cast to your custom target type in your configure methods.
Done! Save your file and run Sharpmake. It should generate a project with a configuration option for the C++ exceptions.