Visual Studio Preprocessor - access compiler flags - c++

I am currently converting a Qt project from being .pro file based to Visual Studio based.
In the original .pro file I have used the following
LIBSTR = '\\"$${LIBS}\\"'
DEFINES += LIBRARIES=\"$${LIBSTR}\"
Which allows me to see what libraries were used in my application.
I would like to replicate this in Visual Studio but cannot see how to do this.

1) Add the configure.h to the project
#pragma once
#define LIBRARIES TEXT("")
2) Add $${LIBS} to the Project properties -> Linker -> Input -> Additional Dependencies section.
3) Add to the Project properties -> Build Events -> Pre-Build Event a script like:
cscript /nologo configure.js $(ProjectPath) $(ConfigurationName) $(PlatformName)
configure.js:
var proj = WScript.Arguments(0);
var conf = WScript.Arguments(1);
var platf = WScript.Arguments(2);
var configFile = "configure.h";
try {
var doc = new ActiveXObject("msxml2.DOMDocument.6.0");
doc.async = false;
doc.resolveExternals = false;
doc.validateOnParse = false;
doc.load(proj);
var node = doc.selectSingleNode("//Configuration[#Name=\""+conf+"|"+platf+"\"]/Tool[#Name=\"VCLinkerTool\"]");
var attr = node.attributes.getNamedItem("AdditionalDependencies");
var libStr = attr ? attr.value : "";
var fso = new ActiveXObject("Scripting.FileSystemObject");
var aFile = fso.GetFile(".\\" + configFile);
var fStream = aFile.OpenAsTextStream(1);
var re = new RegExp("^\\s*#\\s*define\\s+LIBRARIES\\s+");
var done = false;
var text = "";
while (!fStream.AtEndOfStream) {
var aStr = fStream.ReadLine();
if (!done && re.test(aStr)) {
aStr = "#define LIBRARIES TEXT(\"" + libStr.replace(/(\\|")/g, "\\$1") + "\")";
done = true;
}
text += aStr + "\n";
}
fStream.Close();
fStream = aFile.OpenAsTextStream(2);
fStream.Write(text);
fStream.Close();
} catch (e) {
WScript.Echo("Error:");
WScript.Echo(e.description);
WScript.Quit(1);
}
WScript.Echo("Done");
WScript.Quit(0);
Then after you'll start the build the configure.h file will be updated and LIBRARIES will contain all libs you defined for the current build configuration and platform.

Related

Roslyn - save edited document to physical solution

I'm trying to iterate through all documents, change some code and then rewrite analyzed document with new Syntax. The only problem I have it's that i don't know how to save changed document via workspace. After my code execution nothing happens.
var manager = new AnalyzerManager(slnPath);
foreach (var project in manager.Projects.Values)
{
var workspace = project.GetWorkspace();
var sln = workspace.CurrentSolution;
foreach (var msBuildProject in sln.Projects)
{
foreach (var document in msBuildProject.Documents)
{
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
EDIT: Workspace is fetched with help of the Buildalyzer package.
Buildalyzer uses the adhoc workspace which does not persist changes. You want to use the MSBuildWorkspace.
You should have a project file that looks something like this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.3.1" />
</ItemGroup>
</Project>
And then this is how I would load your solution and run your rewriter
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.MSBuild;
class Program {
static async Task Main(string[] args) {
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances.Length == 1
// If there is only one instance of MSBuild on this machine, set that as the one to use.
? visualStudioInstances[0]
// Handle selecting the version of MSBuild you want to use.
: SelectVisualStudioInstance(visualStudioInstances);
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
using (var workspace = MSBuildWorkspace.Create()) {
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
var solutionPath = args[0];
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// Run your custome re-writer on the loaded solution
foreach (var msBuildProject in solution.Projects) {
foreach (var document in msBuildProject.Documents) {
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
}
private static VisualStudioInstance SelectVisualStudioInstance(VisualStudioInstance[] visualStudioInstances) {
Console.WriteLine("Multiple installs of MSBuild detected please select one:");
for (int i = 0; i < visualStudioInstances.Length; i++) {
Console.WriteLine($"Instance {i + 1}");
Console.WriteLine($" Name: {visualStudioInstances[i].Name}");
Console.WriteLine($" Version: {visualStudioInstances[i].Version}");
Console.WriteLine($" MSBuild Path: {visualStudioInstances[i].MSBuildPath}");
}
while (true) {
var userResponse = Console.ReadLine();
if (int.TryParse(userResponse, out int instanceNumber) &&
instanceNumber > 0 &&
instanceNumber <= visualStudioInstances.Length) {
return visualStudioInstances[instanceNumber - 1];
}
Console.WriteLine("Input not accepted, try again.");
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress> {
public void Report(ProjectLoadProgress loadProgress) {
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null) {
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\\:ss\\.fffffff} {projectDisplay}");
}
}
}

Compile to module with Roslyn

I need to compile any C# or VB.NET project to .NetModule. I have following sample code which emits DLLs, Need some help to modify following to get .NetModules out from .csproj
Thanks in advance.
// Required Microsoft.CodeAnalysis 1.3.0
class Program
{
static void Main(string[] args)
{
try
{
//Please copy provide a path to a .csproj
CompileProject(#"C:\WebGoat\WebGoat.NET.csproj", #"C:\tempout").Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
async static Task<string> CompileProject(string projectFilePath, string outputFolderPath)
{
using (var workspace = MSBuildWorkspace.Create())
{
var project = workspace.OpenProjectAsync(projectFilePath).Result;
await Emit(project, outputFolderPath);
return Path.GetFileName(project.OutputFilePath);
}
}
async static Task Emit(Project project, string outputFolderPath)
{
Directory.CreateDirectory(outputFolderPath);
var compilation = await project.GetCompilationAsync();
var outputFilePath = Path.Combine(outputFolderPath, Path.GetFileName(project.OutputFilePath));
var pdbFilePath = Path.ChangeExtension(outputFilePath, "pdb");
var compilationStatus = compilation.Emit(outputFilePath, pdbPath: pdbFilePath);
if (!compilationStatus.Success)
{
Console.WriteLine("Failed.");
}
else
{
Console.WriteLine("Pass.");
}
}
I believe you're looking for CompilationOptions.OutputKind and specifically OutputKind.NetModule.
Something similar to the following should work:
var project = workspace.OpenProjectAsync(projectFilePath).Result;
var options = project.CompilationOptions;
var netModuleOptions = options.WithOutputKind(OutputKind.NetModule);
var projectWithOptions = project.WithCompilationOptions(netModuleOptions);
Now you should be able to get a compilation and emit it as you normally would.
Following fixed the issue
var project = workspace.OpenProjectAsync(projectFilePath).Result;
var options = project.CompilationOptions;
options = options.WithOutputKind(OutputKind.NetModule).WithPlatform(Platform.AnyCpu);
project = project.WithCompilationOptions(options);
var moduleCompilation = await project.GetCompilationAsync();

Microsoft.VisualBasic does not work in VisualStudio 2017 class library project

I am trying to create a class library in VS2017 in which I would like to use the TextFieldParser from Microsoft.VisualStudio. I had to add the direct reference to that dll C:\Program Files (x86)\Reference
I am also using the File class from System.IO
Assemblies\Microsoft\Framework.NETFramework\v4.6.2\Microsoft.VisualBasic.dll
But the build is failing with messages that do not make sense to me, some of which are
The name 'File' does not exist in the current context
The type 'Stream' is defined in an assembly that is not referenced. You must add a reference to assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
The type 'TextReader' is defined in an assembly that is not referenced. You must add a reference to assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
I am not sure if this is due to the new packaging of .NET standard version of the framework
What do I need to do to build the project? Thanks so much!
I have pasted my code below
public async Task<IEnumerable<Security>> GetSecuritiesAsync()
{
string[] lines;
using (var reader = File.OpenText("securities.csv"))
{
var fileText = await reader.ReadToEndAsync();
lines = fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}
//first line header
var securities = new List<Security>();
for (int i = 1; i < lines.Length; i++)
{
if (!string.IsNullOrEmpty(lines[i]))
{
string[] attrs = ParseCsv(lines[i]);
if (attrs.Length == 4)
{
var sec = new Security();
int id;
if (int.TryParse(attrs[0], out id))
{
sec.ID = id;
}
else
{
continue;
}
sec.Symbol = attrs[1];
sec.Name = attrs[2];
sec.Sector = attrs[3];
securities.Add(sec);
}
}
}
return securities;
}
private string[] ParseCsv(string line)
{
TextFieldParser parser = new TextFieldParser(new StringReader(line));
parser.HasFieldsEnclosedInQuotes = true;
parser.SetDelimiters(",");
string[] fields = null;
while (!parser.EndOfData)
{
fields = parser.ReadFields();
}
parser.Close();
return fields;
}
}

Properly babel transpile a file in ember-cli-build withing additional tree

I have some files in my project that I would like to move out of the normal app tree and only load in certain situations. Currently I used broccoli-stew to move the file and broccoli-babel-transpiler to transpile the destination file. However when I do this I end up with an extra default object on the imported files.
this code gets added to the top
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _Ember = _interopRequireDefault(_ember);
and this causes me to have to write the source file with references to ember as Ember["default"].Object etc. Would like to not have any odd references in the source files that makes it harder for other developers to understand.
This is my current ember-cli-build.js file
/* global require, module */
var stew = require('broccoli-stew');
var esTranspiler = require('broccoli-babel-transpiler');
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
storeConfigInMeta: false
});
var additionalTrees = [];
var appTree = app.appAndDependencies();
if (EmberApp.env() !== "production") {
var jQuery = stew.find(appTree, "bower_components/jquery/dist/jquery.min.js");
jQuery = stew.mv(jQuery, "bower_components/jquery/dist/jquery.min.js", "assets/jquery.js");
additionalTrees.push(jQuery);
}
function extractRouter(fileName) {
var router = stew.find(appTree, 'mobile-web/'+ fileName + '.js');
router = esTranspiler(router, {
modules: "amd",
moduleIds: true,
moduleId: "mobile-web/router"
});
router = stew.mv(router, 'mobile-web/'+ fileName + '.js', 'assets/'+ fileName + '.js');
additionalTrees.push(router);
}
extractRouter('router');
extractRouter('secure-router');
return app.toTree(additionalTrees);
};
Try configure your esTranspiler to use modules: "amdStrict":
router = esTranspiler(router, {
modules: "amdStrict",// here
moduleIds: true,
moduleId: "mobile-web/router"
});
It's similar to "commonStrict" as described in the docs. The amdScrict exists in the source code here.

How do I get the output directories from the last build?

Let's say I've got a solution with one or more projects, and I've just kicked off a build using the following method:
_dte.Solution.SolutionBuild.Build(true); // EnvDTE.DTE
How can I get the output paths for each project that just built? For example...
c:\MySolution\Project1\Bin\x86\Release\
c:\MySolution\Project2\Bin\Debug
Please don't tell me this is the only way...
// dte is my wrapper; dte.Dte is EnvDte.DTE
var ctxs = dte.Dte.Solution.SolutionBuild.ActiveConfiguration
.SolutionContexts.OfType<SolutionContext>()
.Where(x => x.ShouldBuild == true);
var temp = new List<string>(); // output filenames
// oh shi
foreach (var ctx in ctxs)
{
// sorry, you'll have to OfType<Project>() on Projects (dte is my wrapper)
// find my Project from the build context based on its name. Vomit.
var project = dte.Projects.First(x => x.FullName.EndsWith(ctx.ProjectName));
// Combine the project's path (FullName == path???) with the
// OutputPath of the active configuration of that project
var dir = System.IO.Path.Combine(
project.FullName,
project.ConfigurationManager.ActiveConfiguration
.Properties.Item("OutputPath").Value.ToString());
// and combine it with the OutputFilename to get the assembly
// or skip this and grab all files in the output directory
var filename = System.IO.Path.Combine(
dir,
project.ConfigurationManager.ActiveConfiguration
.Properties.Item("OutputFilename").Value.ToString());
temp.Add(filename);
}
This makes me want to retch.
You can get to the output folder(s) by traversing the file names in the Built output group of each project in EnvDTE:
var outputFolders = new HashSet<string>();
var builtGroup = project.ConfigurationManager.ActiveConfiguration.OutputGroups.OfType <EnvDTE.OutputGroup>().First(x => x.CanonicalName == "Built");
foreach (var strUri in ((object[])builtGroup.FileURLs).OfType<string>())
{
var uri = new Uri(strUri, UriKind.Absolute);
var filePath = uri.LocalPath;
var folderPath = Path.GetDirectoryName(filePath);
outputFolders.Add(folderPath.ToLower());
}