Data binding generic variable and livedata - android-databinding

How to define Livedata genneric with type List ?
<import type="androidx.lifecycle.LiveData"/>
<import type="java.util.List"/>
<variable
name="livedata"
type="LiveData<List>"/>
when compiling it throw error: incompatible types: Object cannot be converted to List but if i am using primitive type it work , how can i do this with List type?

You must infer the type of list, for example, if list is of type List of Integers you must do:
<import type="androidx.lifecycle.LiveData"/>
<import type="java.util.List"/>
<import type="Integer"/>
<variable
name="livedata"
type="LiveData<List<Integer>>"/>

Related

Meaning of absent <target> element in XLIFF

According to "A. XLIFF Tree Structure" of XLIFF Specification 1.2 <trans-unit> element always has a single <source> element but can have 0 or 1 <target> element. Yet nowhere in the specification I found an explanation what would the absence of <target> element mean? Would it mean that translation is an empty string or that translation is the same text as contained in <source> element?
And in what part of specification (or some other source) that meaning is explained?
There are several reasons for the absence of <target> element and they can be different in various implementation of XLIFF format.
Sdl Trados Studio uses missing <target> elements in units with translate="no" attribute (defined in Xliff namespace) - e.g. when source contains only tags.
It could also mean that the translation unit is not segmented yet, but as I said I think it depends on implementation.

Using MSBuild PropertyGroup outside of Target block

I have a project files collection:
<ItemGroup>
<ApplicationToDeploy
Include="Frontend.WebSite.csproj;11.WebServices.csproj;22.WebServices.csproj"/>
<ApplicationToDeploy
Include="33.WebServices.csproj;44.WebServices.csproj;Workflow55Svc.csproj"/>
</ItemGroup>
I'm trying to get collection of .config-files of these projects:
<Target Name="111">
<PropertyGroup>
<Cfgs>#(ApplicationToDeploy->'%(RootDir)%(Directory)*.config')</Cfgs>
</PropertyGroup>
<ItemGroup>
<InputConfigs Include="$(Cfgs)" />
</ItemGroup>
<Message Text="Cfgs: #(InputConfigs)"/>
</Target>
Inside the Target block all works fine (I see collection of Web.Configs, App.Configs, Log4net.Configs etc.):
Cfgs: C:\Sources\WebServices\11\WebServices\11.WebServices\Web.config;C:\Sources\WebServices\22\WebServices\22.WebServices\web.log4net.config;C:\Sources\WebServices\33\WebServices\33.WebServices\web.environment.config
But I want to initialize this ItemGroup outside of the Target block. Like this:
<PropertyGroup>
<Cfgs>#(ApplicationToDeploy->'%(RootDir)%(Directory)*.config')</Cfgs>
</PropertyGroup>
<ItemGroup>
<InputConfigs Include="$(Cfgs)" />
</ItemGroup>
<Target Name="111">
<Message Text="Cfgs: #(InputConfigs)"/>
</Target>
And when I do this outside of the Target block I get this:
Cfgs: C:\Sources\WebServices\11\WebServices\11.WebServices\*.config;C:\Sources\WebServices\22\WebServices\22.WebServices\*.config;C:\Sources\WebServices\33\WebServices\33.WebServices\*.config
I don't understand what's happens. Is it possible to get the same result outside Target block?
I don't understand what's happens.
This behavior is an effect of the MSBuild evaluation order:
During the evaluation phase of a build:
Properties are defined and modified in the order in which they
appear. Property functions are executed. Property values in the form
$(PropertyName) are expanded within expressions. The property value
is set to the expanded expression.
Item definitions are defined and modified in the order in which they appear. Property functions have already been expanded within expressions. Metadata values are set to the expanded expressions.
Item types are defined and modified in the order in which they appear. Item values in the form #(ItemType) are expanded. Item transformations are also expanded. Property functions and values have already been expanded within expressions. The item list and metadata values are set to the expanded expressions.
During the execution phase of a build:
Properties and items that are defined within targets
are evaluated together in the order in which they appear. Property
functions are executed and property values are expanded within
expressions. Item values and item transformations are also expanded.
The property values, item type values, and metadata values are set to
the expanded expressions."
There's another key point on that link "(...) The string expansion is dependent on the build phase.".
You're using the property 'Cfgs' to recursively map your projects' folders AND to define a wildcard to config files (*.config). When you define 'Cfgs' INSIDE the target, the InputConfigs receives the expanded value of Cfgs (semicolon-separated string list of folders), and just resolve the wildcards. On the other hand, when you define 'Cfgs' OUTSIDE the target, the InputConfigs receives the unexpanded value of Cfgs (#(ApplicationToDeploy->'%(RootDir)%(Directory)*.cs'). When the InputConfigs expands it, it results on the semicolon-separated string list of folders, but it doesn't resolve the wildcards (*.config).
Is it possible to get the same result outside Target block?
I think that InputConfigs should always receive the expanded list of directories. The expansion is made during the execution phase of the build. During this phase, only
properties and items defined within targets are evaluated. So, I would keep all the initialization inside an 'Initialization' Target block. I don't mean it is impossible to do it outside a Target block, but for the reasons mentioned it does not seems logical. =]
Hope this helps,

Using Item functions on metadata values

Background: I manage a fairly large solution. Every so often, people add a DLL reference to a project in the solution where they should've added a project reference. I want to issue a warning in such case. I want to do it by finding all reference with 'bin\debug' in their HintPath*. I know that references are Items in ItemGroup, with metadata "HintPath".
I expected something like this to work:
<Warning Text="Reference %(Reference.Identity) should be a project reference. HintPath: %(Reference.HintPath)"
Condition="%(Reference.HintPath).IndexOf('bin\debug') != -1"/>
However, Seems like I can't use string function IndexOf like that. I tried many permutations of the above, without success.
Edit: I know this check is not full-proof, but I just want to reduce honest mistakes.
Using MSBuild 4.0 Property Functions it is possible to do string comparisons:
<Target Name="AfterBuild">
<Message Text="Checking reference... '%(Reference.HintPath)'" Importance="high" />
<Warning Text="Reference %(Reference.Identity) should be a project reference. HintPath: %(Reference.HintPath)"
Condition="$([System.String]::new('%(Reference.HintPath)').Contains('\bin\$(Configuration)'))" />
</Target>
First not that your syntax is not correct for invoking functions, it would need to be:
%(Reference.HintPath.IndexOf(...)) # Note: not supported by MSBuild
However, property functions in MSBuild are not allowed on item metadata, so that is not going to help you either.
What you could work around this, by invoking a separate target which is basically called for every item.
<Target Name="CheckProjectReferences">
<MSBuild
Projects="$(MSBuildProjectFullPath)"
Properties="Identity=%(Reference.Identity);HintPath=%(Reference.HintPath)"
Targets="_Warn"/>
</Target>
<Target Name="_Warn">
<Warning Text="Reference $(Identity) should be a project reference. HintPath: $(HintPath)"
Condition="$(HintPath.IndexOf('bin\debug')) != -1"/>
</Target>
Frankly, I'm not sure if that is enough to catch all "violations". For example, the above will only work for bin\debug, but not for bin\Debug or other mixed-cased variations, which are functionally equivalent. To look for them as well, you'd need to call the IndexOf(string, StringComparison) overload, however just doing:
$(HintPath.IndexOf('bin\debug', System.StringComparison.OrdinalIgnoreCase))
Will not work, because the MSBuild overload resolution will pick IndexOf(char, Int32) and give you this error:
MSB4184: The expression ""bin\debug".IndexOf(bin\debug, System.StringComparison.OrdinalIgnoreCase)" cannot be evaluated. String must be exactly one character long.
So, you'll need to convince it by using the IndexOf(String, Int32, Int32, StringComparison) overload directly:
$(HintPath.IndexOf('bin\debug', 0, 9, System.StringComparison.OrdinalIgnoreCase))
You may need to also check for bin\Release or other variations. I'm not sure if that is the best way to figure out a reference should be a project reference, but if you know (and to a certain extend control) your environment it might be feasible.
#Christian.K is right in his analysis. Another solution would be to force the overload of type string using " for the quotes:
<Warning
Text="..."
Condition="$(HintPath.IndexOf("bin\debug", System.StringComparison.OrdinalIgnoreCase)) != -1" />

MSBuild tasks can accept primitive arrays, but how do you write one to pass into the task?

I would guess it has to be an ITaskItem since it's a vector instead of scalar, I've got the only 2 MsBuild books here on my desk, and I can't find examples of how to pass in an array to a task. I want to do a string array, but I'd like to know the proper way that would work with any primitive type.
How do you pass in an array of string (or int) to a MsBuild task?
MSBuild tasks can accept ITaskItem, primitives, string or an array of any of those for parameters. You just declare the type in your task and then the values will be converted before passed to the task. If the value cannot convert to the type then an exception will be raised and the build will be stopped.
For example if you have a task which accepts an int[] named Values then you could do.
<Target Name="MyTarget">
<MyTask Values="1;45;657" />
<!-- or you can do -->
<ItemGroup>
<SomeValues Include="7;54;568;432;79" />
</ItemGroup>
<MyTask Values="#(SomeValues) />
</Target>
Both approaches are essentially the same. The other answers stating that all parameters are strings or that you have to use ITaskItem are incorrect.
You said you have two books on MSBuild, then I presume one is my Inside the Microsoft Build Engine book, you should read the chapter on Custom Tasks so that you get a full grasp on these topics. There is a section explaining parameter types specifically.
IIRC, msbuild items are always string arrays - that is the only option. So an array of integers would be stored as an array numeric strings.

MSBuild: how to control the parsing of a semicolon delimited property

When a single property contains semicolons, MSBuild automatically parse the property into a list of properties when used within an itemgroup. Here's a snippet from my project:
<PropertyGroup>
<ConnectionString>workstation id=.;packet size=4096;Integrated Security=SSPI;data source=.;initial catalog=$(SqlDbName)</ConnectionString>
</PropertyGroup>
<ItemGroup>
<InstallShieldProperties Include="
CONNECTIONSTRING=$(ConnectionString);
Another=$(value)"/>
</ItemGroup>
When a task consumes the #(InstallShieldProperties) itemgroup, MSBuild will parse the ConnectionString property into a list of subset properties since it contains semicolons.
foreach (string property in Properties)
{
// Properties array parsed to pieces
}
I know I can change the delimiter of the itemgroup, but that won't make any difference.
I'm trying to avoid manipulating the string[] array within the custom task.
In MSBuild 4.0, you can use $([MSBuild]::Escape($(ConnectionString))).
AFAICS, you can either escape the semicolon in the $(ConnectionString) property like:
<ConnectionString>workstation id=.%3Bpacket size=4096%3B.."</ConnectionString>
Or use some task to replace the ';' in the ConnectionString property to '%3B' and then use that property in InstallShieldProperties item.
The other way could be to change the property type in the custom task from string[] to string, and then split it yourself, the way you want it. You could use enclosing quotes to separate Connection string part from other key/value pairs.
Or if it makes sense for your custom task, then maybe connection string is a special enough property to have as a separate task property.
In MSBuild 4.0, there are now Property Functions. One thing these allow you to do is call .NET String instance methods directly on your properties as if they are strings (which they are).
In your example, instead of using:
$(ConnectionString)
You could use:
$(ConnectionString.Replace(';', '%3B'))
Which will call the String method Replace() to replace semicolons with %3B