I use CAKE 0.21.1.0.
I want to add a task that performs the equivalent of this PowerShell operation:
$dlls = #(${dll1}, ${dll2})
$dlls | % { [Reflection.Assembly]::UnsafeLoadFrom($_) }
[StaticClassFromLocalLibrary]::SomeStaticMethod(SomeArgument)
I have decided not to use the CAKE PowerShell add-in because running the PowerShell script directly throws the error <script>.ps1 is not digitally signed. The script will not execute on the system. Because of my employer's IT policy, I am not permitted to run Set-ExecutionPolicy to circumvent the problem. This is why I want to translate the steps taken in the PowerShell script directly into instructions in CAKE.
Initially, I wrote these lines in my CAKE script:
System.Reflection.Assembly.UnsafeLoadFrom(dll1);
System.Reflection.Assembly.UnsafeLoadFrom(dll2);
StaticClassFromLocalLibrary.SomeStaticMethod(SomeArgument);
However, an exception was thrown, saying that the name StaticClassFromLocalLibrary was not found in the current context.
I then added this line to my CAKE script:
#r #"\\hostName\Folder1\Folder2\Folder3\Folder4\Some.Local.Project.dll"
However, because it was not loaded in an unsafe manner, another exception was thrown, this time informing me that the DLL could not be loaded.
How can I use the #r directive (or any other CAKE command) to specify that I would like the DLL to be loaded in an unsafe manner?
EDIT:
I have solved my problem by consulting this page and adopting the suggestion in the accepted answer.
There's no build in unsafe loading in Cake but as it's just C# what you can do is just convert your PowerShell snippet into C#
var assembly = System.Reflection.Assembly.UnsafeLoadFrom("./tools/Addins/Newtonsoft.Json/lib/net45/Newtonsoft.Json.dll");
var type = assembly.GetType("Newtonsoft.Json.JsonConvert");
var method = type.GetMethod("SerializeObject", new [] {typeof(object) });
var SerializeObject = (Func<object, string>) Delegate.CreateDelegate(
typeof(Func<object, string>), null, method);
var json = SerializeObject(new { Hello = "World"});
Information("{0}", json);
Which will output something like
{"Hello":"World"}
Related
I know it is possible to call C# code from the PowerShell script by loading an assembly. But is there any way to pass and receive a value in between both C# code and PowerShell script.
Let's say I have a $path variable in my power script. I want to pass it to my c# code. And C# code will use the $path. After doing some stuff in the c# code it will return some value to the script. Is this possible? If it is, how can I do it? I must load a third party dll in my power shell and all one or two public methods on that dll to complete some task.
My PowerShell script code:
$scriptpath = $MyInvocation.MyCommand.Path;
$cureentDir = Split-Path $scriptpath;
$isSasDir = $cureentDir -match "mydir";
$requiredFile = "Core.dll";
$myPowersehllVal = "has value for c# code";
My C# code:
$Source = #"
using System.Net;
public class ExtendedWebClient : WebClient
{
String myPowersehllVal;
public int Timeout;
protected override WebRequest GetWebRequest(System.Uri address)
{
}
}
"#;
For getting PS values into C#
https://stackoverflow.com/a/22384009/3546415
In a more general sense, System.Management.Automation (Nuget Required) looks promising, in particular, Runspace and Pipeline.
Here are some good examples of usage:
https://msdn.microsoft.com/en-us/library/ee706576(v=vs.85).aspx
Something like this one seems similar to what you want.
Or maybe just use the PowerShell Class to execute PS commands from your C# module to set PS variable values.
Alternatively, without worrying about interop, you can kind of hack this by working through the file system and/or environmental variables. Following this paradigm, you could even use a memory mapped file and share variables with a broader set of applications. Powershell side would be something like this. For objects, serialization.
I use CSScriptLibrary.dll to execute C# code in my application which runs on both Windows and Linux. Problem is, right now, I need to use #pragma disable warning to disable all kinds of warnings that may arise in order to get the scripts to compile on Mono which is a very ugly hack.
// the following simple script will not execute on Mono due to a warning that a is not used.
var code = "public class Script { public object Run() { var a=1; return 2+3; }}"
// here is how the script is executed using CsScriptLibrary
try
{
var asm = new AsmHelper(CSScript.LoadCode(code, "cs", null, true));
// if we reach that point, the script compiled
var obj = asm.CreateAndAlignToInterface<IScript>("*");
// now run it:
var result=obj.Run();
}
catch (CompilerException e)
{
// on .net compiler exceptions are only raised when there are errors
// on mono I get an exception here, even for warnings like unused variable
}
I already tried to set the default compiler parameters of CSScript to instruct the mono compiler to disregard warnings. Here is what I tried (based on documentation of compiler switches of Mono compiler:
CSScript.GlobalSettings.DefaultArguments = "-warn:0 -warnaserror-";
But I had no success and I am not even sure if this is the right way to go. Anyway, for the sake of completeness I note here that CSScript.GlobalSettings.DefaultArguments defaults to /c /sconfig /co:/warn:0 in CSScript.
Does anyone know how to get CSScript.LoadCode to disregard warnings on Mono or at least not treat them as errors?
Here are two solutions (found with help of Oleg Shilo). You can either include the desired compiler option directly in the script:
//css_co -warn:0
using System;
...
or you can substitute CSScript.LoadCode with LoadWithConfig, which allows passing compiler options directly. Something like this:
static public Assembly LoadCode(string scriptText, bool debugBuild, params string[] refAssemblies)
{
string tempFile = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() +".cs";
try
{
using (StreamWriter sw = new StreamWriter(tempFile))
sw.Write(scriptText);
return LoadWithConfig(scriptFile, null, debugBuild, CSScript.GlobalSettings, "-warn:0", refAssemblies);
}
finally
{
if (!debugBuild)
{
//delete temp file
}
}
}
It should be noted, that the second solution will bypass the built in assembly caching performed in LoadCode. It is easy enough to cache the compiled script object though.
I'm trying to execute a Python script from within a C# application but when it tries to launch the script, I receive the error ImportException was unhandled No module name csv. I checked the Ironpython folder and there is a csv.py file...?
Code I'm using to call the script:
IDictionary<string, object> options = new Dictionary<string, object>();
options["Argument"] = new[] { filePath, profile };
var pyEngine = Python.CreateEngine(options);
var pyScope = pyEngine.CreateScope();
string script = "xccdf-xml2tsv.py";
pyScope.SetVariable(profile, options);
pyEngine.ExecuteFile(script, pyScope);
python file:
#!/usr/bin/env python
###
# (C) 2010 Adam Crosby
# Licensed under:
# http://creativecommons.org/licenses/by-nc-sa/3.0/
##
import csv
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import xml.etree.ElementTree as ET
xmlns = "http://checklists.nist.gov/xccdf/1.1"
...
The IronPython engine you created is unaware of the standard library implementation it should use. You can specify it by adding something like
var paths = pyEngine.GetSearchPaths();
paths.Add(#"C:\Program Files (x86)\IronPython 2.7\Lib");
pyEngine.SetSearchPaths(paths);
You could either point it to a locally installed iron python (as in my example) or use the appropriate NuGet package to directly add it to your project. You might also have to deploy it to your output folder/installer/...
Please also take note of this answer as well as this answer and comments as they might provide additional information.
I'm trying to run an MSBuild task, in this case ResolveAssemblyReferences, and get access to the outputs of the task such as ResolvedFiles. A short F# script that loads the project (a default F# project created with VS2013) and runs the task is below. With the log verbosity set to Diagnostic, I can see that the task runs successfully, and resolves all the assemblies correctly.
#r #"C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Build.dll"
#r #"C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Build.Framework.dll"
open System
open Microsoft.Build
let p = new Evaluation.Project("d:/dev/fsharptest/Test/Test.fsproj")
let log = Logging.ConsoleLogger()
log.Verbosity <- Framework.LoggerVerbosity.Diagnostic
p.Build([|"ResolveProjectReferences";"ResolveAssemblyReferences"|],
Seq.singleton (log :> Framework.ILogger) )
for i in p.AllEvaluatedProperties do
printfn "%s: %s" i.Name i.EvaluatedValue
However, neither the evaluated properties nor the items contain any of the outputs of ResolveAssemblyReferences, which is what I am after. The file Microsoft.Common.CurrentVersion.targets has as one output of ResolveAssemblyReferences <Output TaskParameter="ResolvedFiles" ItemName="ReferencePath"/>, but I cannot access this value.
How should I go about getting hold of it?
It turns out that Evaluation.Project and Execution.ProjectInstance are rather different. I tried to use the former, but the latter is closest to the obsolete BuildEngine.Project class I was previously using. The following code snippet returns the fully resolved references for a given project file:
#r #"C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Build.dll"
#r #"C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Build.Framework.dll"
open System
open Microsoft.Build
let p = new Execution.ProjectInstance(#"d:\dev\fsharptest\Test\Test.fsproj")
p.Build([|"ResolveReferences"|], new Collections.Generic.HashSet<_>())
for i in p.GetItems("ReferencePath") do
printfn "%s" i.EvaluatedInclude
It is in fact possible to get an arbitrary output for the target in question in this manner.
I think the problem is that some arbitrary Output parameter of the Task you end up running is not what the MSBuild task itself returns. It gathers up the "target Returns" of the Tasks you specify directly.
However, I don't know exactly how your syntax works here: you are giving Task names rather than Targets?
But based on what I've read (entry 37 in Kretzler’s book) you could have a Target defined to run the desired Task and hook up the task output to the target’s Return attribute. Then the MSBuild task, told to run that target, will pass through the Return attribute as its own.
I think that would be something like:
<Target Name="ResolveAssemblyReferences" ⋯
Returns="#(ReferencePath)" >
So if the Task you are calling from within that Target is populating the Item Array named ReferencePath as its output parameter, then you publish that same item array as the Target's return value.
If you don't use Returns anywhere in the build script, then the Outputs are automatically taken as the returns.
If you can't edit ResolveAssemblyReferences, then I said you can make a new Target which depends on it. Since ReferencePath is global after the task completes, the new target will still see them and can return it.
If all else fails, have your build script write the item list to a file, which you can then load from some other program without concern over what MSBuild is returning.
I am experimenting with the Roslyn script engine. Using the following code, I set up my script engine.
var csharpEngine = new ScriptEngine();
csharpEngine.AddReference("System");
csharpEngine.AddReference(this.GetType().Assembly.Location);
scriptSession = csharpEngine.CreateSession();
Then I execute a script with the following line:
scriptSession.Execute(script);
The script contains a very simple reference to a static function on a class in my assembly.
private string script =
#"using System;
using RoslynWindow;
Hello.SayHello();";
In the output window the function merely prints to the console. So I have shown I can call into a public static member of my assembly without passing a "HostObjectModel" to the script engine. I want to prevent this from happening. I'd like to be able to register only specific members (functions, variables or properties) to be accessed by the script engine, and no others.
Any idea how to accomplish this?
Traverse the AST with Roslyn to check if the script tries to call anything you don't allow.