I have a flash project which, for optimization purposes, has to have constant references replaced with literals during release build.
There are hundreds of constants which I want to replace. All of them are stored in single file, in this format:
FILE: Constants.as
public static const CONST_1 :uint = 0;
public static const CONST_LOLO :int = -1;
public static const CONST_WHEE :Number = 2.55;
public static const OTHER_CONST :String = "La string!";
public static const ITSAMEMARIO :String = "O, HAI!";
public static const MAGE_WALL :uint = 15;
I figure I could do it manually, like that:
<replaceregexp match="CONST_1" replace="0">
<fileset dir="${project.sourcePath}" includes="**/*.as" />
</replaceregexp>
<replaceregexp match="CONST_LOLO" replace="-1">
<fileset dir="${project.sourcePath}" includes="**/*.as" />
</replaceregexp>
And so on, for all the other variables. The problem is twofold - first of all, it is quite a lot of work. But the bigger problem is, that these constants can change and I'd have to remember to do the change in two places.
Generally I am using Ant (which I just started to learn too) to accomplish this task, but if you think there is a better way, I am all ears. There are two solutions I can think of, none of which I know how to execute:
Write some smarty-pants piece of Ant code which would parse this constants file and happily do the replaces keeping everything in memory.
Make the task first parse the Constants.as, output a new Ant script, which then will be executed by the first task.
I am using Flash Builder 4.5 for all my Ant needs.
EDIT:
Some clarification. In the project I am using Constants, for example LEVEL_WIDTH. All of these constants are declared in the aforementioned Constants.as. Now what I want is to replace all of the instances of these constants in the whole project with their actual value. So such line:
return (x >= 0 && x < Constants.LEVEL_WIDTH);
will be replaced by:
return (x >= 0 && x < 20);
OK this is not the easiest thing to do with ant. First you need to know what you can change. This means all the names of your constants as well as the corresponding values. Are the constant names unique? If yes this sounds like a map structure to me. Then you need to regexreplace all your source files which contain one or more of these variables so that every constant is replaced with the actual value. This is not what ant is designed for but you can do it with a script def.
I would do this with java like this :
Store all constant/values into a map (if the constants are unique) else use a different structure.
Sample code :
<project name="test" default="build">
<property name="constants" value="constants.txt"/>
<scriptdef name="replaceConstants" language="java">
<attribute name="constants" />
<attribute name="srcFile" />
<![CDATA[
import java.util.*;
import java.util.regex.*;
ArrayList constantNameList = new ArrayList();
ArrayList constantValueList = new ArrayList();
var constantFile = attributes.get("constants");
Pattern regex = Pattern.compile("(?<=const)\\s+(\\b\\w+\\b).*?=\\s*(.*?)\\s*;");
Matcher regexMatcher = regex.matcher(constantFile);
while (regexMatcher.find()) {
constantNameList.add(regexMatcher.group(1));
constantValueList.add(regexMatcher.group(2));
}
for(int i = 0; i < constantNameList.size(); ++i)
{
//debugging
System.out.print("key : ");
System.out.print(constantNameList.get(i));
System.out.print(" value : ");
System.out.println(constantValueList.get(i));
//do the actual replacement here
}
]]>
</scriptdef>
<target name="build">
<loadfile property="constants.file" srcFile="${constants}"/>
<loadfile property="source.file" srcFile="sourceFile.txt"/>
<echo message="${constants.file}"/>
<replaceConstants constants="${constants.file}" srcFile="${source.file}"/>
</target>
</project>
You will need java 1.6 or later to run this as well as http://www.java2s.com/Code/Jar/ABC/bsh-2.0b5.jar.htm
This jar to run it.
Output :
[replaceConstants] key : CONST_1 value : 0
[replaceConstants] key : CONST_LOLO value : -1
[replaceConstants] key : CONST_WHEE value : 2.55
[replaceConstants] key : OTHER_CONST value : "La string!"
[replaceConstants] key : ITSAMEMARIO value : "O, HAI!"
[replaceConstants] key : MAGE_WALL value : 15
So what I did is stored all the constant names/value into two arrays. You need to iterate through the arrays and regex replace for each of your source files. The whole thing can be a macrodef which you can call multiple times.
Related
I am rather new to F# but I have a question about creating and reading custom configuration file. I know how it would look in c# so for example I have a simple config file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="customSection" type="Tool.Lib.Config, Tool.Lib" />
</configSections>
<communicationSection>
<responseTimeoutInMs value="900000" />
</communicationSection>
</configuration>
Basing on that in c# it is simple. I am creating model named Config with properties marked as ConfigurationProperty (witch relates to xml node name, in this case responseTimeoutInMs) something like:
[ConfigurationProperty("responseTimeoutInMs")]
public ResponseTimeoutConfigElement ResponseTimeoutInMs
{
get => (ResponseTimeoutConfigElement)base["responseTimeoutInMs"];
set => base["responseTimeoutInMs"] = value;
}
And of course value is set as ConfigurationElement so:
public class ResponseTimeoutConfigElement : ConfigurationElement
{
[ConfigurationProperty("value", IsRequired = true, IsKey = true, DefaultValue = 0)]
public int Value => (int)base["value"];
}
It is a nice mechanism, I can pin converters into it and create types I need while reading configuration.
I know i can read default config using ConfigurationManager and exe configuration map, but this is basic config reading using key and value.
So my question is, is there something similar in F# to this in C#?
I'm not sure if it's quite what you're after as you mention using a "custom configuration file", but in the past I've used the AppSettings Type Provider to get strongly typed access to app variables.
If that's not appropriate, there's also a more standard XML type provider that might help?
I find type providers really useful in avoiding having to write boilerplate code for simple access, and they're a really nice feature of F# development.
You can do pretty much the same thing in F# as in C#:
type ResponseTimeoutConfigElement() =
inherit ConfigurationElement()
[<ConfigurationProperty("value", IsRequired = true, IsKey = true, DefaultValue = 0)>]
member this.Value = base.["value"] :?> int
type Config() =
inherit ConfigurationSection()
[<ConfigurationProperty("responseTimeoutInMs")>]
member this.ResponseTimeInMs
with get() = base.["responseTimeoutInMs"] :?> ResponseTimeoutConfigElement
and set (value: ResponseTimeoutConfigElement) = base.["responseTimeoutInMs"] <- value
We have the following test set up.
Permutations.Tests.fsproj
<ItemGroup>
<Compile Include="Permute1Tests.fs" />
<Compile Include="Permute2Tests.fs" />
</ItemGroup>
Permute1Tests.fs
module Permute1Tests
open Xunit
open Permutations.Permute1
[<Theory>]
[<MemberData("permuteTestValues")>]
let ``permute`` (x, expected) =
let actual = permute x
Assert.Equal<List<int>>(expected, actual);
let permuteTestValues : obj array seq =
seq {
yield [| [0;1]; [[0;1]; [1;0]] |]
}
Permute2Tests.fs
module Permute2Tests
open Xunit
open Permutations.Permute2
[<Theory>]
[<MemberData("removeFirstTestData")>]
let ``removeFirst`` (item, list, expected: List<int>) =
let actual = removeFirst list item
Assert.Equal<List<int>>(expected, actual)
let removeFirstTestData : obj array seq =
seq {
yield [| 0; [1;2;3;4]; [1;2;3;4] |]
}
When we run dotnet test, this is the error:
System.InvalidOperationException : Test data returned null for Permute2Tests.removeFirst. Make sure it is statically initialized before this test method is called.
Oddly enough, Permute1Tests.fs runs without error. Its test passes. And, if we swap the Permute1Test.fs position in the ItemGroup with the Permute2Test.fs, then the latter now works and the former has the error.
How do we statically initialize test data before calling a test method? It seems that ItemGroup order matters in our current approach, and that makes our current approach fail.
The full version of the above code is here.
Edit: ILSpy Output
Permute1Tests.fs.cs
// <StartupCode$Permutations-Tests>.$Permute1Tests
using <StartupCode$Permutations-Tests>;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
internal static class $Permute1Tests
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal static readonly IEnumerable<object[]> permuteTestValues#12;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal static int init#;
static $Permute1Tests()
{
IEnumerable<object[]> permuteTestValues =
$Permute1Tests.permuteTestValues#12 =
(IEnumerable<object[]>)new Permute1Tests.permuteTestValues#14(0, null);
}
}
Permute2Tests.fs.cs
// <StartupCode$Permutations-Tests>.$Permute2Tests
using <StartupCode$Permutations-Tests>;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
internal static class $Permute2Tests
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal static IEnumerable<object[]> removeFirstTestData#15;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal static int init#;
public static void main#()
{
IEnumerable<object[]> removeFirstTestData =
$Permute2Tests.removeFirstTestData#15 =
(IEnumerable<object[]>)new Permute2Tests.removeFirstTestData#17(0, null);
}
}
Permutations.Test.fsproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Compile Include="Permute1Tests.fs" />
<Compile Include="Permute2Tests.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Permutations\Permutations.fsproj" />
</ItemGroup>
</Project>
Why
This has to do with the fact that your assembly is an "executable" (i.e. a program with an entry point) rather than a "library".
F# compiles executables slightly differently than libraries: in the module where the entry point is located, all static data is initialized inside the main function, before executing everything else; in all other modules, static data is initialized in static constructors. I am not sure what was the reasoning behind this decision, but this is how the F# compiler behaves.
Next, how does the F# compiler determine which module contains the entry point? Very simple: whichever module is the last one, that's where the entry point is. To think of it, this is the only sensible choice: since F# has compilation order, only the very last file can have access to definitions in all other files; therefore, that's where the entry point must be.
So, in your example, whichever module was last in the list, ended up with a main function, in which the static initialization code was located. And since the unit test runner doesn't run the entry point before executing tests, the static data in that module remained uninitialized.
Solution 1: add an artificial module to contain the entry point
As you have already discovered yourself, one solution is to add an artificial module that contains nothing but the entry point. That way, the test module won't be the last one anymore, won't contain the entry point, and therefore its data will be initialized in the static constructor.
The artificial module doesn't even have to have an [<EntryPoint>] main function, it could just be this:
module Dummy
let _x = 0 // `do ()` would be even shorter, but that will create a warning
The compiler will add an entry point anyway.
Solution 2: compile to netstandard2.0
If you switch your target from netcoreapp2.0 to netstandard2.0, your assembly will be considered a "library" rather than an "executable", and the compiler won't add an entry point, and won't put static initialization in it.
I am localizing my UWP app in several languages using the Multilingual App Toolkit
here are some screen shots of how the MultilingualResourcesFolder and the Strings folder looks like in the project
Multilingual Resources
Strings
All the files are filled with the correct values.
In the application I retrieve the list of available languages with
Windows.Globalization.ApplicationLanguages.ManifestLanguages and iterate through them. The iteration yields these results
pt-PT
ro-RO
ru
sk
sl
This is very strange because the languages are declared in exaclty the same way but for some languages the language code includes the locale and for some it doesn't , for no reason I can undestand.
In the way I declare them the iteration should yield
pt-PT
ro-RO
ru-RU
sk-SK
sl-SL
I have tried manually declaring the languages in the appxmanifest by replacing x-generate like this
<Resources>
<Resource Language="ru-ru"/>
<Resource Language="sk-sk"/>
<Resource Language="pt-pt"/>
<Resource Language="sk-sk"/>
<Resource Language="sl-sl"/>
</Resources
but the results are the same.
I need to get the application to recognize the locale as well as the language for every language.
Can anybody help?
Ok, I've got a solution for you. There's also a separate issue that you're dealing with, which is why x-generate in the manifest isn't doing the right thing (or is it?). I'm investigating that on the side.
So, this is the method you want:
public static string GetLocaleFromLanguage(string identifier)
{
int result;
StringBuilder localeName = new StringBuilder(500);
result = LocaleFunctions.ResolveLocaleName(identifier, localeName, 500);
StringBuilder localeDisplay = new StringBuilder(500);
result = LocaleFunctions.GetLocaleInfoEx(localeName.ToString(), LCTYPE.LOCALE_SENGLISHDISPLAYNAME, localeDisplay, 500);
return localeDisplay.ToString();
}
And of course, you're going to need the externs:
class LocaleFunctions
{
// pInvoke declarations
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int GetLocaleInfoEx(String lpLocaleName, LCTYPE LCType, StringBuilder lpLCData, int cchData);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int ResolveLocaleName(string lpNameToResolve, StringBuilder lpLocaleName, int cchLocaleName);
}
Unfortunately, you're also going to need the declaration for LCTYPE. The code for that can be found at this link: http://www.pinvoke.net/default.aspx/Enums/LCTYPE.html
(It's a massive chunk of code and I don't want to just copy that here.
In my code, I manually added the following language resources to my manifest (again, I'm not sure why x-generate isn't adding the Russian locales here, but this works around it):
<Resources>
<Resource Language="EN-US" />
<Resource Language="ES-ES" />
<Resource Language="ES-MX" />
<Resource Language="PT-PT" />
<Resource Language="RO-RO" />
<Resource Language="RU-RU" />
<Resource Language="RU-UA" />
</Resources>
And this is the method I'm using to call in:
private void button_Click(object sender, RoutedEventArgs e)
{
string output = "";
foreach (string thisManifestLanguage in Windows.Globalization.ApplicationLanguages.ManifestLanguages)
{
output += thisManifestLanguage + ": ";
string ResolvedName = LocalizationTesting.LocaleTest.GetLocaleFromLanguage(thisManifestLanguage);
output += ResolvedName + "\n";
}
this.outputText.Text = output;
}
When I called this, the output I got was:
en-US: English (United States)
es-ES: Spanish (Spain, International Sort)
es-MX: Spanish (Mexico)
pt-PT: Portuguese (Portugal)
ro-RO: Romanian (Romania)
ru: Russian (Russia)
ru-UA: Russian (Ukraine)
I should point out that in GetLocaleFromLanguage, localeName for "ru" gets "ru-RU", so if that's what you're looking for, that's available too.
Let me know if this doesn't meet with your needs. I'll see if I can get a reason about why in my case, I had to explicitly add ru-UA to my manifest.
I'll let Dante follow up, but I'm curious why this list is interesting? Eg: What's the user scenario?
A UWP lists the languages it can provide data for, and the Region & Language Settings collects a list of languages that the user understands. So then Windows figures out which language best matches the user and app language and the resource loader uses that. So I'm curious what scenario is missing?
How can I make a robust datamapper script in xml if the target stored-procedure have some parameters added but indeed with default values ??
For instance,
I designed a stored-procedure with some default parameters like this,
CREATE PROCEDURE [dbo].[SP_Test_1]
#mode int = 1 -- skippable
AS
RETURN 1
MyBatis datamapper in xml like this,
<procedure id="myDBService.exeSPTest1">
SP_Test_1
</procedure>
The way I called this statement,
IList<myStruct> list = myDataSource.QueryForList<myStruct>("myDBService.exeSPTest1", null);
But always got errors like this,
[ArgumentOutOfRangeException: index]
IBatisNet.DataMapper.Configuration.ParameterMapping.ParameterPropertyCollection.get_Item(Int32 index) +88
IBatisNet.DataMapper.Configuration.ParameterMapping.ParameterMap.GetProperty(Int32 index) +76
IBatisNet.DataMapper.Commands.DefaultPreparedCommand.ApplyParameterMap(ISqlMapSession session, IDbCommand command, RequestScope request, IStatement statement, Object parameterObject) +395
IBatisNet.DataMapper.Commands.DefaultPreparedCommand.Create(RequestScope request, ISqlMapSession session, IStatement statement, Object parameterObject) +439
IBatisNet.DataMapper.MappedStatements.MappedStatement.ExecuteQueryForList(ISqlMapSession session, Object parameterObject) +125
IBatisNet.DataMapper.SqlMapper.QueryForList(String statementName, Object parameterObject) +251
until I gave a parameterMap tag and then works,
<procedure id="myDBService.exeSPTest1" parameterMap="myDBService.params-exeSPTest1">
SP_Test_1
</procedure>
<parameterMap id="myDBService.params-exeSPTest1" class="Hashtable">
<parameter column="mode" property="mode" dbType="int" type="int" />
</parameterMap>
Hashtable ht = new Hashtable();
ht.Add("mode", 1);
IList<myStruct> list = myDataSource.QueryForList<myStruct>("myDBService.exeSPTest1", ht);
Although it worked afterwards, I actually want flexible parameters inputted by lots of procedure calls. For example, in the same procedure, I can make it have multi parameters without change any front-tier code, like this,
CREATE PROCEDURE [dbo].[SP_Test_1]
#mode int = 1, -- skippable
#reserved int = 1 -- used in the future, still skippable
AS
RETURN 1
The point is do NOT change and front-tier code or xml settings if I add skippable parameters in the stored-procedure. Any idea will be appreciated.
Thank you.
I think I got an idea from this page.
for example,
<statement id="myDBService.exeSPTest1" parameterClass="myDBService.params-exeSPTest1" >
<dynamic>
// EXEC SP_Test_1 #mode = #mode#
// or
// EXEC SP_Test_1
// as ur wish
</dynamic>
</statement>
Now, it can work with a SP with any number of default parameters.
How to generate an HTML report from PartCover results .xml
There is a tool you can use to generate a HTML report:
https://github.com/danielpalme/ReportGenerator
Here you can find an article how to integrate the tool into MSBuild:
http://www.palmmedia.de/Blog/2009/10/30/msbuild-code-coverage-analysis-with-partcover-and-reportgenerator
To my knowledge, there is no convenient tool like NCoverExplorer that can transform a PartCover results .xml file into a .html report, but there are some .xsl files that can be used to transform PartCover's results to .html in CruiseControl.NET: Using CruiseControl.NET with PartCover.
You could take those .xsl files from CruiseControl.NET and convert your PartCover results.xml using something like Sandcastle's XslTransform.exe.
By the way, if this happens to be related to TeamCity, the upcoming 5.0 release will include support for .NET coverage using PartCover or NCover. See the documentation for more informations. Otherwise ignore this paragraph ;-)
Easiest solution is probably to use msxsl, a simple command line transformer. I use it for exactly this purpose, and it's easy to integrate into your build system.
http://www.microsoft.com/downloads/details.aspx?FamilyID=2FB55371-C94E-4373-B0E9-DB4816552E41&displaylang=en
Maybe a complicated way of doing it, but I did this with the Simian xml report. Created an XSL file for the formatting, then wrote a dumb little console application;
private const string MissingExtension = "Please enter a valid {0} file, this is missing the extension.";
private const string InvalidExtension = "Please enter a valid {0} file, the file provided has an invalid extension.";
public static void Main(string[] args)
{
if (args.Length < 2)
{
System.Console.WriteLine("Please enter a xsl file and xml file full path.");
return;
}
var xslFile = args[0];
var xmlFile = args[1];
if (!CheckFileNameFormat(xslFile, false))
return;
if (!CheckFileNameFormat(xmlFile, true))
return;
var transform = new XslCompiledTransform();
// Load the XSL stylesheet.
transform.Load(xslFile);
// Transform xml file into an html using the xsl file provided.
transform.Transform(xmlFile, xmlFile.Replace("xml", "html"));
}
private static bool CheckFileNameFormat(string fileName, bool isXmlFile)
{
var extension = isXmlFile ? "xml" : "xsl";
// valida that the argument has a period
if (fileName.IndexOf(".") < 1)
{
System.Console.WriteLine(string.Format(MissingExtension, extension));
return false;
}
var filePieces = fileName.Split('.');
// split on the period and make sure the extension is valid
if (filePieces[filePieces.GetUpperBound(0)] != extension)
{
System.Console.WriteLine(string.Format(InvalidExtension, extension));
return false;
}
return true;
}
Then I can call it from a MSBuild file like so;
<Target Name="RunSimian" DependsOnTargets="RebuildSolution">
<Exec IgnoreExitCode="true" Command=""$(MSBuildProjectDirectory)\..\Build\Packages\Simian\simian-2.2.24.exe" -formatter=xml:$(MSBuildProjectDirectory)\..\Build\Artifacts\simian.xml -language=cs -excludes=$(MSBuildProjectDirectory)\..\Product\Production\**\*.Designer.cs $(MSBuildProjectDirectory)\Production\**\*.cs" >
</Exec>
<Exec IgnoreExitCode="true" Command=""$(MSBuildProjectDirectory)\..\Build\Packages\XmlToHtmlConverter.exe" $(MSBuildProjectDirectory)\..\Build\Packages\Simian\simian.xsl $(MSBuildProjectDirectory)\..\Build\Artifacts\simian.xml">
</Exec>