Change base path on VS publish - visual-studio

I have a blazorWASM project. While developing, I need index.html to have it's base path set to <base href="/" />, but when I use the built-in publish to file, the output is set to land directly into my PHP project which acts as a host that serves WASM static files, and needs this base path: <base href="/wwwroot/" />.
Is there a way to have them automatically switched so I do not keep forgetting to do so? Alternatively, how do I configure the project so that it will work while I debug it on IIS with the wwwroot base path?

You need to add a build task to your CSPROJ Blazor WASM.
Important note: you need Newtonsoft 13.0.1, System.Text.Json isn't supported.
<UsingTask TaskName="ReplaceBaseHRef" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<InputFilename ParameterType="System.String" Required="true" />
<AppSettingsfile ParameterType="System.String" Required="true" />
<BaseHRefAttribute ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="$(NugetPackageRoot)\newtonsoft.json\13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Text" />
<Using Namespace="System.Text.RegularExpressions" />
<Using Namespace="Newtonsoft.Json" />
<Using Namespace="Newtonsoft.Json.Linq" />
<Code Type="Fragment" Language="C#">
<![CDATA[
var inputFile = File.ReadAllText(InputFilename);
var appsetting = File.ReadAllText(AppSettingsfile);
JObject appsettings = JObject.Parse(appsetting);
var baseHRef = appsettings[BaseHRefAttribute].Value<string>();
if (!string.IsNullOrEmpty(baseHRef)) {
Log.LogMessage( MessageImportance.High, baseHRef );
var outputFile = InputFilename;
var matchExpression = "\\<base\\ href=\\\"(.*)\\\"\\ \\/\\>";
var newBaseHRef = $"<base href=\"{baseHRef}\" />";
File.WriteAllText(
outputFile,
Regex.Replace(inputFile, matchExpression, newBaseHRef)
);
}
]]>
</Code>
</Task>
</UsingTask>
<Target Name="ReplaceBaseHRef" AfterTargets="Publish">
<ReplaceBaseHRef InputFilename="$(PublishDir)wwwroot\index.html" AppSettingsfile="$(PublishDir)wwwroot\appsettings.Production.json" BaseHRefAttribute="BaseHRef" />
</Target>
Now take your appsettings.Production.json and add the configuration setting as:
{
"BaseHRef": "/client/",
...
}
now launch the Publish (in folder, IIS or on Azure).
Your index.html will contain <base href="/client/" />.

Related

MSBuild: How to access a property value set by a Target during the Post Build event in Visual Studio

I have a PostBuild event which invokes a batch file and I need to pass in a particular parameter into the batch file. This parameter is populated through another task which is invoked through a Target configured to run before the PostBuildEvent.
I can see that it gets successfully displayed when displayed using the element as part of the section.
But $(TargetFrameworkToolsFolderPath) under the PostBuildEvent has an "" value. Is there a way to access this custom property in the post build event?
Example:
<UsingTask TaskName="GetTargetFrameworkToolsFolderName" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<SDKFolderPath ParameterType="System.String" Required="true" />
<TargetFrameworkVersionStr ParameterType="System.String" Required="true" />
<TargetFrameworkToolsFolder ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
TargetFrameworkToolsFolder = SDKFolderPath + "\\" + "bin\\NETFX " + TargetFrameworkVersionStr.Substring(1) + " Tools\\";
</Code>
</Task>
</UsingTask>
<Target Name="FindTargetFrameworkToolsFolderPath" BeforeTargets="PostBuildEvent">
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="SdkPath" />
</GetFrameworkSdkPath>
<GetTargetFrameworkToolsFolderName SDKFolderPath="$(SdkPath)" TargetFrameworkVersionStr="$(TargetFrameworkVersion)">
<Output PropertyName="TargetFrameworkToolsFolderPath" TaskParameter="TargetFrameworkToolsFolder"/>
</GetTargetFrameworkToolsFolderName>
<Message Text="$(TargetFrameworkToolsFolderPath)" Importance="normal" /> --> Displayed correctly here
</Target>
<PropertyGroup>
<PostBuildEvent>
call $(ProjectDir)AfterBuildCommands.bat $(TargetFrameworkToolsFolderPath) --> The TargetFrameworkToolsFolderPath property value here seems to be empty.
</PostBuildEvent>
</PropertyGroup>
But $(TargetFrameworkToolsFolderPath) under the PostBuildEvent has an
"" value. Is there a way to access this custom property in the post
build event?
In fact, <PostBuildEvent> is a property and MSBuild reads all the properties first and then executes all targets.
If you put these below outside the target which defines the property TargetFrameworkToolsFolderPath, these below will always execute first, as expected, the values of TargetFrameworkToolsFolderPath will be empty.
To avoid it, you should put the PostBuildEvent and TargetFrameworkToolsFolderPath properties in the same target and make sure the target is executed early enough, such as, run after PrepareForBuild target.
<PropertyGroup>
<PostBuildEvent>
call $(ProjectDir)AfterBuildCommands.bat $(TargetFrameworkToolsFolderPath) --> The TargetFrameworkToolsFolderPath property value here seems to be empty.
</PostBuildEvent>
</PropertyGroup>
Solution
Try this below:
<UsingTask TaskName="GetTargetFrameworkToolsFolderName" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<SDKFolderPath ParameterType="System.String" Required="true" />
<TargetFrameworkVersionStr ParameterType="System.String" Required="true" />
<TargetFrameworkToolsFolder ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
TargetFrameworkToolsFolder = SDKFolderPath + "\\" + "bin\\NETFX " + TargetFrameworkVersionStr.Substring(1) + " Tools\\";
</Code>
</Task>
</UsingTask>
<Target Name="MyFindTargetFrameworkToolsFolderPath" AfterTargets="PrepareForBuild">
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="SdkPath" />
</GetFrameworkSdkPath>
<GetTargetFrameworkToolsFolderName SDKFolderPath="$(SdkPath)" TargetFrameworkVersionStr="$(TargetFrameworkVersion)">
<Output PropertyName="TargetFrameworkToolsFolderPath" TaskParameter="TargetFrameworkToolsFolder" />
</GetTargetFrameworkToolsFolderName>
<PropertyGroup>
<PostBuildEvent> call $(ProjectDir)AfterBuildCommands.bat $(TargetFrameworkToolsFolderPath) --> The TargetFrameworkToolsFolderPath property value here seems to be empty.</PostBuildEvent>
</PropertyGroup>
<Message Text="$(TargetFrameworkToolsFolderPath)" Importance="normal" />
</Target>
Hope it could help you.

can't initialize speechRecognition on cordova

I have an app written in Keno UI (Telerik). I'm using build.phonegap.com to build runtime for my app, using cordova and Kendo. I'm stuck on getting the speechrecognition plugin to initialize. I'm sure it's something stupid but I'm not sure what it is.
Below is a copy of my config.xml file:
<?xml version='1.0' encoding='utf-8'?>
<widget id="ca.xyz.mmb" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>mmb</name>
<description>
Integrated Mobile Billing for XYZ Customers
</description>
<author email="support#xyz.ca" href="http://xyz.ca">
Go Team
</author>
<content src="index.html" />
<plugin name="cordova-plugin-compat" spec="^1.2.0" />
<plugin name="cordova-plugin-file" spec="^4.3.3" />
<plugin name="cordova-plugin-speechrecognition" spec="1.2.0" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
<engine name="ios" spec="^4.3.1" />
</widget>
Below is a copy of the relevant portion of the build:
Build Date: 2018-10-16 05:38:36 +0000
--------------------------------------------------------------------------------
PLUGIN OUTPUT
--------------------------------------------------------------------------------
Fetching plugin "cordova-plugin-compat#^1.2.0" via npm
Installing "cordova-plugin-compat" at "1.2.0" for android
Fetching plugin "cordova-plugin-file#^4.3.3" via npm
Installing "cordova-plugin-file" at "4.3.3" for android
Plugin dependency "cordova-plugin-compat#1.2.0" already fetched, using that version.
Dependent plugin "cordova-plugin-compat" already installed on android.
The Android Persistent storage location now defaults to "Internal".
Please check this plugin's README to see if your application needs any changes in its config.xml.
If this is a new application no changes are required.
If this is an update to an existing application that did not specify an "AndroidPersistentFileLocation" you may need to add:
"<preference name="AndroidPersistentFileLocation" value="Compatibility" />"
to config.xml in order for the application to find previously stored files.
Fetching plugin "cordova-plugin-speechrecognition#1.2.0" via npm
Installing "cordova-plugin-speechrecognition" at "1.1.2" for android
--------------------------------------------------------------------------------
PROJECT PROPERTIES
Below is the initalization code for the program.
(function () {
var bootstrap = function () {
$(function () {
alert("running bootstrap");
app.mobileApp = new kendo.mobile.Application(document.body, {
transition: 'slide',
skin: 'flat',
initial: 'components/home/view.html',
statusBarStyle: 'black-translucent',
layout: 'main'
});
alert("speech init "); //this is the last alert that pops up
window.plugins.speechRecognition.isRecognitionAvailable(
function(result) {
useSpeech = result ; alert("speech");
},
function(err) {
useSpeech = false; alert(err);
}
);
alert("done speech init");
});
};
bootstrap();
The place where I try to run isRecognitionAvailable is where it fails. I'm not sure why. I've tried putting in an alert for window.plugins just to see what it says but that always comes back undefined. A search on google implies that that is normal, so that doesn't help me much.

How to get property value of a project file using msbuild

Using MSBuild I include a solution>
<ItemGroup>
<ProjectToBuild Include="$(SVNLocalPath)\$(SolutionName)"> </ProjectToBuild>
</ItemGroup>
I need to include all the *.csproj file from the solution with the condition of the proj file contain or define a property; for example if x.csproj contain a defined property "TestProjectType" would like to include the project into my itemGroup
something like this
<Target Name = "TestProperties">
<Message Text="TestProperties"/>
<ItemGroup>
<AllProj Include="$(SVNLocalPath)\*.csproj"/>
<AllTestProj Include="%(AllProj.Identity)" Condition="%(AllProj.ProjectTypeGuids)=={3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"/>
</ItemGroup>
<Message Text="#(AllTestProj )"/>
</Target>
Thanks
You can achieve that through custom task.
A simple sample to check a property (test) in all projects exclude current project of current solution:
<UsingTask TaskName="GetPropertyTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup>
<ProjectFile ParameterType="System.String" Required="true" />
<BuildOutput ParameterType="System.String[]" Output="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Xml"/>
<Reference Include="Microsoft.Build"/>
<Using Namespace="Microsoft.Build" />
<Using Namespace="Microsoft.Build.Evaluation" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var properties = new Dictionary<string, string>
{
{ "Configuration", "$(Configuration)" },
{ "Platform", "$(Platform)" }
};
//Log.LogMessage(MessageImportance.High, "customLog");
// Log.LogMessage(MessageImportance.High, ProjectFile);
var collection = new ProjectCollection(properties);
var project = collection.LoadProject(ProjectFile);
ProjectProperty pp = project.Properties.Where(p => p.Name == "MyCustomProperty").FirstOrDefault();
string customValue = pp==null?"empty":pp.EvaluatedValue;
BuildOutput = new String[] { customValue };
]]></Code>
</Task>
</UsingTask>
<Target Name="AfterBuild">
<GetPropertyTask ProjectFile="%(ProjectToScan.FullPath)">
<Output ItemName="ProjectToScanOutput" TaskParameter="BuildOutput"/>
</GetPropertyTask>
<Message Text="ClassLibrary1" Importance="high" Condition="'%(ProjectToScanOutput.Identity)' == 'test'" />
</Target>
More information, please refer to this article.

UnitTask error trying to use Microsoft.Build.Evaluation

have the following code inside my CSProj file:
<UsingTask TaskName="HelloWorld" TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<Task>
<!--Microsoft.Build.dll-->
<Using Namespace="Microsoft.Build.Evaluation" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var p = new Project("$(MSBuildProjectFullPath)");
]]>
</Code>
</Task>
</UsingTask>
in my AfterBuild I call it like this:
<Target Name="AfterBuild">
<HelloWorld />
</Target>
The error I am getting is:
error CS0246: The type or namespace name 'Project' could not be found
(are you missing a using directive or an assembly reference?)
Referring to var p = new Project
Include Microsoft.Build reference in the task can fix that issue. (The Microsoft.Build.dll reference need to be added to your project firstly)
<Task>
<Reference Include="Microsoft.Build"/>
<Using Namespace="Microsoft.Build.Evaluation" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var p = new Project("$(MSBuildProjectFullPath)");
]]>
</Code>
</Task>

Nesper adding event types to my winforms app.config

I'm interested in defining multiple event-types my app.config file but it doesn't appear to get loaded by default. Is there something that I'm doing wrong? The event type doesn't exist within com.espertech.esper.client.Configuration.
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net" />
<section name="esper-configuration" type="com.espertech.esper.util.EsperSectionHandler,NEsper" />
</configSections>
<esper-configuration>
<engine-settings>
<defaults>
<threading>
<listener-dispatch preserve-order="false" timeout-msec="2000" locking="suspend" />
<insert-into-dispatch preserve-order="false" timeout-msec="3000" locking="suspend" />
<internal-timer enabled="false" msec-resolution="1234567" />
<thread-local style="fast" />
</threading>
<event-meta>
<class-property-resolution style="distinct_case_insensitive" />
</event-meta>
<view-resources>
<share-views enabled="false" />
</view-resources>
<logging>
<execution-path enabled="true" />
</logging>
<variables>
<msec-version-release value="30000" />
</variables>
</defaults>
</engine-settings>
<event-type name="Products" class="ProtoProduct"/>
<event-type name="MarketDepths" class="ProtoDepth"/>
<event-type name="MarketTrades" class="ProtoTrade"/>
<event-type name="Orders" class="ProtoOrder"/>
<event-type name="Positions" class="ProtoPosition"/>
<auto-import import-name="org.mycompany.mypackage.MyUtility"/>
<auto-import import-name="org.mycompany.util.*"/>
</esper-configuration>
The most likely issue is that you haven't used the fully qualified name of the class. In your examples, the classes have no namespace. If your classes are in a namespace add those to the class attribute in your config. If for some reason that isn't the issue, it is most likely that the tips are not visible within the AppDomain. Just make sure they are built into your assembly.

Resources