MSBuild: Copy files conditional output paths - msbuild

I currently have a solution with the following project layout (simplified and condensed for this example)
src
├──2019
| └── Project.2019
| └── Input
| └── 1.txt
└──2020
└── Project.2020
└── Input
└── 1.txt
And I would like to copy all the input files to the following hierarchy
Input
├──2019
| └── 1.txt
└──2020
└── 1.txt
With [System.IO.Directory]::GetDirectories I am able to match the directories I want, but I'm not sure how to extract the year, and set DestinationFolder correctly using the Copy task

Does Project.2019 and Project.2020 include projects?
I have made the assumption that it does, following this structure:
directory.build.props
│
└───src
│ CopyFiles.sln
│
├───2019
│ └───Project.2019
│ │ Program.cs
│ │ Project.2019.csproj
│ │
│ └───input
│ 1.txt
│ 2.txt
│
└───2020
└───Project.2020
│ Program.cs
│ Project.2020.csproj
│
└───input
1.txt
2.txt
Having a project file in each of those folders, allows us to execute a custom build target, under the context of each project. This grants a simple way of building the list of files (includes) and allows us to go up a folder from the MSBuildProjectDirectory to get the year.
The final output path for the input files (InputOutputPath), assumes the existance of OutputRootPath and OutputPath is used to overwrite the default msbuild folders. I typically do this to have all output in \output. But you can customize these as you wish.
It also expects each project that needs files copied to have CopyInputFiles specified to true. This can also be done using a condition, in case you want it to always copy files if there are any in project\input*.txt
<PropertyGroup>
<CopyInputFiles>true</CopyInputFiles>
</PropertyGroup>
directory.build.props:
<Target Name="CopyFiles" Condition="'$(CopyInputFiles)' == 'true'" >
<ItemGroup>
<InputFiles Include="$(MSBuildProjectDirectory)\input\*.txt" />
</ItemGroup>
<PropertyGroup>
<Year>$([System.IO.Directory]::GetParent($(MSBuildProjectDirectory)).Name)</Year>
<InputOutputPath>$(OutputRootPath)\$(Configuration)\Input\$(Year)\</InputOutputPath>
</PropertyGroup>
<Copy SourceFiles="#(InputFiles)" DestinationFolder="$(InputOutputPath)" />
</Target>
Example output:
PS e:\repos\CopyFiles> msbuild .\src\CopyFiles.sln /t:copyfiles
Microsoft (R) Build Engine version 16.8.2+25e4d540b for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.
Building the projects in this solution one at a time. To enable parallel build, please add the "-m" switch.
Build started 12/6/2020 12:56:07 PM.
Project "E:\repos\CopyFiles\src\CopyFiles.sln" on node 1 (copyfiles target(s)).
ValidateSolutionConfiguration:
Building solution configuration "Debug|Any CPU".
Project "E:\repos\CopyFiles\src\CopyFiles.sln" (1) is building "E:\repos\CopyFiles\src\2019\Project.2019\Project.2019.csproj" (2) on node 1 (copyfiles target(s)).
CopyFiles:
Creating directory "E:\repos\CopyFiles\Drops\Debug\Input\2019".
Creating directory "E:\repos\CopyFiles\Drops\Debug\Input\2019".
Copying file from "E:\repos\CopyFiles\src\2019\Project.2019\input\1.txt" to "E:\repos\CopyFiles\Drops\Debug\Input\2019\1.txt".
Copying file from "E:\repos\CopyFiles\src\2019\Project.2019\input\2.txt" to "E:\repos\CopyFiles\Drops\Debug\Input\2019\2.txt".
Done Building Project "E:\repos\CopyFiles\src\2019\Project.2019\Project.2019.csproj" (copyfiles target(s)).
Project "E:\repos\CopyFiles\src\CopyFiles.sln" (1) is building "E:\repos\CopyFiles\src\2020\Project.2020\Project.2020.csproj" (3) on node 1 (copyfiles target(s)).
CopyFiles:
Creating directory "E:\repos\CopyFiles\Drops\Debug\Input\2020".
Copying file from "E:\repos\CopyFiles\src\2020\Project.2020\input\1.txt" to "E:\repos\CopyFiles\Drops\Debug\Input\2020\1.txt".
Creating directory "E:\repos\CopyFiles\Drops\Debug\Input\2020".
Copying file from "E:\repos\CopyFiles\src\2020\Project.2020\input\2.txt" to "E:\repos\CopyFiles\Drops\Debug\Input\2020\2.txt".
Done Building Project "E:\repos\CopyFiles\src\2020\Project.2020\Project.2020.csproj" (copyfiles target(s)).
Done Building Project "E:\repos\CopyFiles\src\CopyFiles.sln" (copyfiles target(s)).
With the resulting structure:
Drops
└───Debug
├───AnyCPU
│ ├───Project.2019
│ │ └───netcoreapp3.1
│ └───Project.2020
│ └───netcoreapp3.1
└───Input
├───2019
│ 1.txt
│ 2.txt
│
└───2020
1.txt
2.txt
You can find a fully working example in: https://github.com/Kencdk/msbuild_copyfiles_example
You can hook it up to your build, by setting the target to execute after any other build target that is being executed in your projects.
eg:
<Target Name="CopyFiles" Condition="'$(CopyInputFiles)' == 'true'" AfterTargets="Build">

Related

ESP-IDF project with multiple source files

I started my project with a simple "blink" example and used it as a template to write my code.
This example used only one source file blink.c.
Eventually, I want to a use multi source files project and can't figure out how to configure CMakeLists.txt in order to compile the project.
My CMakeLists.txt is:
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(blink)
I want to add for example init.c.
I tried different ways, but with no success.
None of idf_component_register() / register_component() worked for me.
Any idea how to correctly configure the project?
Right, the CMake project hierarchy in ESP IDF is a bit tricky. You are looking at the wrong CMakeLists.txt file. Instead of the one in root directory, open the one in blink/main/CMakeLists.txt. This file lists the source files for the "main" component, which is the one you want to use. It would look like this:
idf_component_register(SRCS "blink.c" "init.c"
INCLUDE_DIRS ".")
Make sure your init.c file is in the same directory as this CMakeLists.txt and blink.c.
I also recommend taking a look at the Espressif Build System documentation, it's quite useful.
You should edit the CMakeLists.txt located in your main folder inside your project folder. In addition, you need to put the directory that contains the header files into INCLUDE_DIRS parameter.
For example, if you have this file structure in your project (you're putting init.h inside include folder) as shown below:
blink/
├── main/
│ ├── include/
│ │ └── init.h
│ ├── blink.c
│ ├── CMakeLists.txt
│ ├── init.c
│ └── ...
├── CMakeLists.txt
└── ...
The content in your main/CMakeLists.txt should be:
idf_component_register(SRCS "blink.c" "init.c"
INCLUDE_DIRS "." "include")

How do I get log4j.properties to read environment variables?

I have multiple environments (dev/qa/prod) for my application. I would therefore like to differentiate the log conversion pattern based on environment. I have an env variable set which stores which environment the application is running it. But, how do I get log4j.properties to read this env variable?
This is my what my current properties file looks like:
log4j.rootLogger = INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss}] my-api.%-5p: %m%n
I have tried following the log4j lookup docs, but this still does not include the environment in my log file.
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd
HH:mm:ss}] ${env:ENVIRONMENT}-my-api.%-5p: %m%n
The output looks like this:
[2018-01-22 14:17:20] -my-api.INFO : some-message.
But I want it to look like this:
[2018-01-22 14:17:20] dev-my-api.INFO : some-message.
You may also try a pattern that has become some sort of standard in Luminus and other frameworks. You create an env directory that holds prod/dev/test subfolders with some additional code and resources. In your lein project, for each profile you specify where to find those files in addition to the default path.
As the result, you've got three different log settings. Each of them will be loaded depending on what are you doing. When just develop the code -- from env/dev/resources/log4j.properties and when running tests -- from env/test/resources/log4j.properties.
Here is an example:
$ tree env
.
├── dev
│ └── resources
│ └── log4j.properties
├── prod
│ └── resources
│ └── log4j.properties
└── test
└── resources
└── log4j.properties
Some bits from the project.clj:
:profiles {:dev {:plugins [[autodoc/lein-autodoc "1.1.1"]]
:dependencies [[org.clojure/clojure "1.8.0"]
[log4j/log4j "1.2.17"]]
:resource-paths ["env/dev/resources"]}}
For test profile, you probably may want to specify both dev and test paths.

msbuild: build as to a appxbundle (AppxBundle=Always not working)

I have a shared Windows8.1 project with a Phone and Desktop project in it. I defined different configurations to build x86/x64 for desktop and ARM for phone.
msbuild works fine without error, but there is no final *.appxbundle file on the output folder (or anywhere else) although i set the parameter AppxBundle=Always.
my command looks like this:
msbuild myApp.sln /p:OutputPath=%OUTPATH%;Configuration=Phone;Platform=ARM;AppxBundle=Always;AppxBundlePlatforms=ARM
/t:Rebuild,Publish
The output is:
OUTPATH
├── ForBundle
│ └── AppxManifest.xml
├── AppxManifest.xml
├── App.WindowsPhone.build.appxrecipe
├── App.WindowsPhone_3.2.1_ARM.appx
├── App.WindowsPhone_3.2.1_scale-100.appx
├── App.WindowsPhone_3.2.1_scale-140.appx
├── App.WindowsPhone_3.2.1_scale-180.appx
├── resources.pri
└── SomeDependency.winmd
I tried to pack this folder with makeappx.exe bundle but this didn't work and I realized the folder looks a bit different to what is into a appxbundle.
Creating a appxbundle via VS GUI is no problem, but I would like to automate that step!
Thanks in advance!
There's a hint comment in Microsoft.AppXPackage.Targets:
When building on the command line or in TFS (determined by looking at the $(BuildingInsideVisualStudio) property), if build is
invoked on an
app package-producing project, the package for the project will be produced as part of building the project without specifying
any additional
flags or targets. This is control by an MSBuild property named GenerateAppxPackageOnBuild which is set to true by default.
If $(BuildingInsideVisualStudio) = false and $(GenerateAppxPackageOnBuild) = true, then build will also produce a
package.
true
FYI, the file has moved for VS 2022, new location isL
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VisualStudio\v17.0\AppxPackage

Visual Studio Online Build Fails on Dependent Project NuGet References

I am using Visual Studio Online for my repo and build. Here is my TFS hierarchy.
<ClassLibraries>
│
├───<Solution 1>
│ └───Build.Proj
│ └───.NuGet
│ └───NuGet.exe
│ └───NuGet.Config
│ └───NuGet.Targets
│ └───<Project 1> // Common Code
│ └───Project1.csproj
│ └───<Project 2> // Common Code Unit Test
│ └───Project2.csproj
│
├───<Solution 2>
│ └───Build.Proj
│ └───.NuGet
│ └───NuGet.exe
│ └───NuGet.Config
│ └───NuGet.Targets
│ └───<Project 3>
│ └───Project1.csproj
│ └───<Project 4>
│ └───Project2.csproj
I have a build definition for Solution 1 which builds succesfully, restoring the NuGet packages as required without issue.
The Solution 2.sln file includes Project 1 from Solution 1 as a dependency, as both Project 3 and Project 4 have code dependencies on it (Solution 1 is my common code solution, which is included in just about everything).
The build definition for Solution 2 fails because it cannot find the NuGet dependencies for Project 1. I guess the RestorePackages target in the Build.Proj is ignoring the dependent project.
<Target Name="RestorePackages">
<Exec Command="$(MSBuildThisFileDirectory).NuGet\NuGet.exe restore %(Solution.Identity)" />
</Target>
I can't fathom out why. If I execute "msbuild build.proj" locally on Solution 2 then it cleans and builds all projects succesfully.
I have worked out that my build.proj file needs to reference directly Solution 1 in this example:
<ItemGroup>
<Solution Include="$(MSBuildThisFileDirectory)*.sln" />
<Solution Include="$(MSBuildThisFileDirectory)..\Solution1\*.sln" />
</ItemGroup>
This then causes MSBuild to go through the NuGet package restore for the dependent project. Problem solved. Sort of..
After I had deleted all my local code and performed a Get Latest from the repo, I realised that VS2013 exhibits the behaviour I originally saw. So I need now to work out how to make the IDE build in the same way as the Build.Proj file I am using for VSO and msbuild executed from the command line.

cmake find_path doesnot work on windows

on my first cmake project it looks like the find_path does not work on windows when im trying to reference package libcurl. im using cmake 2.8.12.2.
i took a look at FindCURL.cmake source code and tried below:
if(WIN32)
set(CMAKE_PREFIX_PATH
"${WIN32_LIBS_DIR}/libcurl"
"${WIN32_LIBS_DIR}/libcurl/include"
"${WIN32_LIBS_DIR}/libcurl/lib"
)
find_path(CURL_INCLUDE_DIR NAMES curl/curl.h) # this line is copied from the first line of FindCURL.cmake source
message("WIN32_LIBS_DIR: ${WIN32_LIBS_DIR}")
message("thu ${CURL_INCLUDE_DIR}")
and get the result of
WIN32_LIBS_DIR: ../../../lwqq_root/win32-dev
"thu CURL_INCLUdE_DIR_NOTFOUND",
by the way, the variable WIN32_LIBS_DIR is set to win32-dev which looks like below:
└─win32-dev
├─libcurl
│ ├─bin
│ ├─include
│ │ └─curl
│ │ └─curl.h
│ ├─lib
│ │ └─pkgconfig
│ └─samples
any idea why?
The modern way to use CURL is to use the imported targets created by FindCURL and to guide the search with CURL_ROOT, like so:
set(CURL_ROOT "${WIN32_LIBS_DIR}")
find_package(CURL REQUIRED)
add_executable(my_target main.cpp)
target_link_libraries(my_target PRIVATE CURL::libcurl)
See: https://cmake.org/cmake/help/latest/module/FindCURL.html
Your question does not include enough detail to reproduce your issue. Here is what I did to try:
cmake_minimum_required(VERSION 3.20)
project(test LANGUAGES NONE)
# Create the directory structure in the question:
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/win32-dev/libcurl/bin")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/win32-dev/libcurl/include/curl")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/win32-dev/libcurl/lib/pkgconfig")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/win32-dev/libcurl/samples")
file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/win32-dev/libcurl/include/curl/curl.h" "")
# Set WIN32_LIBS_DIR to the root just created
set(WIN32_LIBS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/win32-dev")
# Your code:
set(
CMAKE_PREFIX_PATH
"${WIN32_LIBS_DIR}/libcurl"
"${WIN32_LIBS_DIR}/libcurl/include"
"${WIN32_LIBS_DIR}/libcurl/lib"
)
find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
message(STATUS "WIN32_LIBS_DIR: ${WIN32_LIBS_DIR}")
message(STATUS "CURL_INCLUDE_DIR ${CURL_INCLUDE_DIR}")
This was on Windows with CMake 3.20, but I am sure it would work going back. I tested this on Linux with every CMake version between 3.0 and 3.20 and it worked on all of them.
Here's what I see in the terminal:
D:\test>cmake -S . -B build
-- Building for: Visual Studio 16 2019
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.19043.
-- WIN32_LIBS_DIR: D:/test/win32-dev
-- CURL_INCLUDE_DIR D:/test/win32-dev/libcurl/include
-- Configuring done
-- Generating done
-- Build files have been written to: D:/test/build
D:\test>tree /F win32-dev
Folder PATH listing for volume Sources
Volume serial number is 0000008F 943F:828D
D:\TEST\WIN32-DEV
└───libcurl
├───bin
├───include
│ └───curl
│ curl.h
│
├───lib
│ └───pkgconfig
└───samples
As you can see from the output, I have mirrored your directory layout exactly and the same settings of other variables has allowed find_path to succeed, even on Windows.