Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
635f324
init
shuaiyuanxx Mar 27, 2026
208e9ec
update
shuaiyuanxx Mar 27, 2026
af041e4
update
shuaiyuanxx Mar 27, 2026
e5f990d
resolve comments
shuaiyuanxx Mar 27, 2026
e00d4ed
add placeholder for wslc save step.
shuaiyuanxx Mar 30, 2026
16dbf44
Merge branch 'feature/wsl-for-apps' into user/shawn/buildUX
shuaiyuanxx Mar 30, 2026
f5c1c75
Merge branch 'feature/wsl-for-apps' into user/shawn/buildUX
shuaiyuanxx Mar 30, 2026
14e0dbf
merge cmake file
shuaiyuanxx Mar 30, 2026
9554539
Apply suggestion from @Copilot
shuaiyuanxx Mar 30, 2026
d3dc0e6
remove comments for wslc save
shuaiyuanxx Mar 31, 2026
1c29367
Merge branch 'user/shawn/buildUX' of https://github.com/microsoft/WSL…
shuaiyuanxx Mar 31, 2026
ad910d2
Merge branch 'feature/wsl-for-apps' into user/shawn/buildUX
shuaiyuanxx Apr 1, 2026
13251c7
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/…
shuaiyuanxx Apr 9, 2026
86e9cce
removed image saving from target file
shuaiyuanxx Apr 9, 2026
da0b8f5
resolve comments
shuaiyuanxx Apr 9, 2026
96eca90
change marker file output dir
shuaiyuanxx Apr 9, 2026
c7edfb9
resolve comments
shuaiyuanxx Apr 9, 2026
53c269b
update
shuaiyuanxx Apr 9, 2026
99ca823
resolve comments
shuaiyuanxx Apr 9, 2026
e7eb811
update build message
shuaiyuanxx Apr 9, 2026
a80b6c4
init
shuaiyuanxx Mar 27, 2026
1e124d1
update
shuaiyuanxx Mar 27, 2026
877c466
update
shuaiyuanxx Mar 27, 2026
2a6429c
resolve comments
shuaiyuanxx Mar 27, 2026
80ae30b
add placeholder for wslc save step.
shuaiyuanxx Mar 30, 2026
b1121b1
merge cmake file
shuaiyuanxx Mar 30, 2026
c086f9a
remove comments for wslc save
shuaiyuanxx Mar 31, 2026
4e67f36
Apply suggestion from @Copilot
shuaiyuanxx Mar 30, 2026
1b9b788
removed image saving from target file
shuaiyuanxx Apr 9, 2026
a4d4e54
resolve comments
shuaiyuanxx Apr 9, 2026
1bdf948
change marker file output dir
shuaiyuanxx Apr 9, 2026
bd8f39c
resolve comments
shuaiyuanxx Apr 9, 2026
c3932b9
update
shuaiyuanxx Apr 9, 2026
43eedbf
resolve comments
shuaiyuanxx Apr 9, 2026
4e18045
update build message
shuaiyuanxx Apr 9, 2026
4069041
resolve comments
shuaiyuanxx Apr 10, 2026
a397487
Merge branch 'user/shawn/buildUX' of https://github.com/microsoft/WSL…
shuaiyuanxx Apr 10, 2026
678dcb3
Apply suggestion from @Copilot
shuaiyuanxx Apr 10, 2026
628d355
Apply suggestion from @Copilot
shuaiyuanxx Apr 10, 2026
2353272
Apply suggestion from @Copilot
shuaiyuanxx Apr 10, 2026
61da5bc
resolve comments
shuaiyuanxx Apr 10, 2026
aef23fd
Merge branch 'user/shawn/buildUX' of https://github.com/microsoft/WSL…
shuaiyuanxx Apr 10, 2026
fff7279
Update nuget/Microsoft.WSL.Containers/build/Microsoft.WSL.Containers.…
shuaiyuanxx Apr 10, 2026
be4b56c
resolve comments
shuaiyuanxx Apr 11, 2026
ee11459
Merge branch 'feature/wsl-for-apps' into user/shawn/buildUX
shuaiyuanxx Apr 13, 2026
40f6148
Merge branch 'feature/wsl-for-apps' into user/shawn/buildUX
shuaiyuanxx Apr 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,126 @@
<ItemGroup>
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\runtimes\win-$(WSLCSDK_Platform)\wslcsdk.dll" />
</ItemGroup>
</Project>

<!-- ================================================================== -->
<!-- Container Image Build Targets -->
<!-- ================================================================== -->

<!-- Default properties -->
<PropertyGroup>
<!-- Auto-detect wslc CLI from MSI install path. Override with WslcCliPath property if needed. -->
<WslcCliPath Condition="'$(WslcCliPath)' == '' AND Exists('$(ProgramW6432)\WSL\wslc.exe')">$(ProgramW6432)\WSL\wslc.exe</WslcCliPath>
Comment thread
shuaiyuanxx marked this conversation as resolved.
<!-- Unified intermediate directory: $(IntDir) for C++, $(IntermediateOutputPath) for .NET -->
<_WslcIntDir Condition="'$(IntDir)' != ''">$(IntDir)</_WslcIntDir>
<_WslcIntDir Condition="'$(_WslcIntDir)' == '' AND '$(IntermediateOutputPath)' != ''">$(IntermediateOutputPath)</_WslcIntDir>
<_WslcIntDir Condition="'$(_WslcIntDir)' == ''">obj\</_WslcIntDir>
</PropertyGroup>

<!-- Default metadata for WslcImage items -->
<ItemDefinitionGroup>
<WslcImage>
<Image></Image>
<Tag>latest</Tag>
</WslcImage>
Comment thread
shuaiyuanxx marked this conversation as resolved.
</ItemDefinitionGroup>

<!-- Check that wslc CLI is installed (invoked via DependsOnTargets from WslcBuildAllImages) -->
<Target Name="WslcCheckDependencies">

<Exec Command="&quot;$(WslcCliPath)&quot; --version"
IgnoreExitCode="true"
ConsoleToMSBuild="true"
StandardOutputImportance="low"
StandardErrorImportance="low">
<Output TaskParameter="ExitCode" PropertyName="_WslcExitCode" />
</Exec>

<Error Condition="'$(_WslcExitCode)' != '0'"
Code="WSLC0001"
Text="The wslc CLI at '$(WslcCliPath)' could not be found or did not run successfully. Install WSL by running: wsl --install --no-distribution" />
</Target>

<!-- Validate required metadata on WslcImage items (invoked via DependsOnTargets from WslcBuildAllImages) -->
<Target Name="_WslcValidateItems">

<Error Condition="'%(WslcImage.Dockerfile)' == ''"
Code="WSLC0002"
Text="WslcImage '%(WslcImage.Identity)' is missing required Dockerfile metadata." />

<Error Condition="'%(WslcImage.Context)' == ''"
Code="WSLC0003"
Text="WslcImage '%(WslcImage.Identity)' is missing required Context metadata." />

<!-- Check for characters invalid in Windows file names: / \ : * ? " < > |
XML entities: &quot; = " &lt; = < &gt; = > -->
<Error Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(WslcImage.Identity)', '[/\\:*?&quot;&lt;&gt;|]'))"
Comment thread
shuaiyuanxx marked this conversation as resolved.
Code="WSLC0004"
Text="WslcImage The item value '%(WslcImage.Identity)' must be a valid file name." />
Comment thread
shuaiyuanxx marked this conversation as resolved.
</Target>

<!-- Dispatch each WslcImage via MSBuild task instead of batching directly because:
1. Sources may contain semicolons (multi-directory) which conflict with Properties syntax
2. Wildcard expansion in Inputs requires an intermediate ItemGroup (_WslcCollectSources)
that must run per-image in a separate evaluation context -->
<Target Name="WslcBuildAllImages"
AfterTargets="Build"
DependsOnTargets="WslcCheckDependencies;_WslcValidateItems"
Condition="'@(WslcImage)' != '' AND '$(DesignTimeBuild)' != 'true'">

<MSBuild Projects="$(MSBuildProjectFullPath)"
Targets="_WslcBuildSingleImage"
Comment thread
shuaiyuanxx marked this conversation as resolved.
Properties="Configuration=$(Configuration);Platform=$(Platform);_WslcName=%(WslcImage.Identity);_WslcImage=%(WslcImage.Image);_WslcTag=%(WslcImage.Tag);_WslcDockerfile=%(WslcImage.Dockerfile);_WslcContext=%(WslcImage.Context);_WslcSourceDir=$([MSBuild]::Escape('%(WslcImage.Sources)'));_WslcIntDir=$(_WslcIntDir)" />
</Target>
Comment thread
shuaiyuanxx marked this conversation as resolved.
Comment thread
shuaiyuanxx marked this conversation as resolved.

<!--
Collect source files for incremental check.
Wildcards are expanded here in ItemGroup Include (not in Inputs attribute or MSBuild task Properties,
where they get escaped). Sources can be semicolon separated directory paths;
each is expanded with **\* to collect all files recursively.
If Sources is omitted, fall back to tracking the full Context directory so changes to
.dockerignore and other Docker build inputs still invalidate the marker.
-->
<Target Name="_WslcCollectSources">
<ItemGroup>
<_WslcSourceDirs Include="$([MSBuild]::Unescape('$(_WslcSourceDir)').Split(';'))" Condition="'$(_WslcSourceDir)' != ''" />
<_WslcSourceFiles Include="$(_WslcDockerfile)" />
<_WslcSourceFiles Include="%(_WslcSourceDirs.Identity)\**\*" Condition="'%(_WslcSourceDirs.Identity)' != ''" />
Comment thread
shuaiyuanxx marked this conversation as resolved.
<_WslcSourceFiles Include="$(_WslcContext)\**\*" Condition="'$(_WslcSourceDir)' == '' AND '$(_WslcContext)' != ''" />
</ItemGroup>
</Target>

<!-- Inner target: build a single image with incremental check -->
<Target Name="_WslcBuildSingleImage"
DependsOnTargets="_WslcCollectSources"
Inputs="@(_WslcSourceFiles)"
Outputs="$(_WslcIntDir)wslc_$(_WslcName).built">

Comment thread
shuaiyuanxx marked this conversation as resolved.
Comment thread
shuaiyuanxx marked this conversation as resolved.
<MakeDir Directories="$(_WslcIntDir)" />

<!-- Resolve image reference: use Image metadata if set, otherwise fall back to Identity -->
<PropertyGroup>
<_WslcImageRef Condition="'$(_WslcImage)' != ''">$(_WslcImage)</_WslcImageRef>
<_WslcImageRef Condition="'$(_WslcImageRef)' == ''">$(_WslcName)</_WslcImageRef>
</PropertyGroup>

<Message Importance="high"
Text="WSLC: Building image '$(_WslcImageRef):$(_WslcTag)'..." />

<Exec Command="&quot;$(WslcCliPath)&quot; image build -t &quot;$(_WslcImageRef):$(_WslcTag)&quot; -f &quot;$(_WslcDockerfile)&quot; &quot;$(_WslcContext)&quot;"
ConsoleToMSBuild="true" />

<Touch Files="$(_WslcIntDir)wslc_$(_WslcName).built" AlwaysCreate="true" />

<Message Importance="high"
Text="WSLC: Image '$(_WslcImageRef):$(_WslcTag)' built successfully." />
</Target>

<!-- Clean container artifacts -->
<Target Name="WslcClean"
AfterTargets="Clean"
Condition="'@(WslcImage)' != ''">

<Delete Files="$(_WslcIntDir)wslc_%(WslcImage.Identity).built" />
</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,49 @@
</ItemDefinitionGroup>

<Import Project="$(MSBuildThisFileDirectory)..\Microsoft.WSL.Containers.common.targets" />
</Project>

<!-- ================================================================== -->
<!-- C++ specific: Write .tlog files for VS Fast Up-to-Date Check -->
<!-- ================================================================== -->

<Target Name="_WslcWriteAllTlogs"
AfterTargets="WslcBuildAllImages"
Condition="'@(WslcImage)' != '' AND '$(TLogLocation)' != ''">

<MSBuild Projects="$(MSBuildProjectFullPath)"
Targets="_WslcWriteSingleTlog"
Properties="Configuration=$(Configuration);Platform=$(Platform);_WslcName=%(WslcImage.Identity);_WslcDockerfile=%(WslcImage.Dockerfile);_WslcContext=%(WslcImage.Context);_WslcSourceDir=$([MSBuild]::Escape('%(WslcImage.Sources)'));_WslcIntDir=$(_WslcIntDir);_WslcTlogDir=$([MSBuild]::EnsureTrailingSlash('$(TLogLocation)'))" />
Comment thread
shuaiyuanxx marked this conversation as resolved.
</Target>

<!-- Reuses _WslcCollectSources from common.targets to get @(_WslcSourceFiles) -->
<Target Name="_WslcWriteSingleTlog" DependsOnTargets="_WslcCollectSources">
<PropertyGroup>
<_WslcTlogToolPath>$([System.IO.Path]::GetFullPath('$(WslcCliPath)'))</_WslcTlogToolPath>
<_WslcMarkerFullPath>$([System.IO.Path]::GetFullPath('$(_WslcIntDir)wslc_$(_WslcName).built'))</_WslcMarkerFullPath>
Comment thread
shuaiyuanxx marked this conversation as resolved.
</PropertyGroup>

<WriteLinesToFile
File="$(_WslcTlogDir)wslc.$(_WslcName).read.1.tlog"
Lines="^$(_WslcTlogToolPath);@(_WslcSourceFiles->'%(FullPath)')"
Overwrite="true"
Encoding="Unicode" />

<WriteLinesToFile
File="$(_WslcTlogDir)wslc.$(_WslcName).write.1.tlog"
Lines="^$(_WslcTlogToolPath);$(_WslcMarkerFullPath)"
Overwrite="true"
Comment thread
shuaiyuanxx marked this conversation as resolved.
Encoding="Unicode" />
</Target>

<!-- Clean tlog files -->
<Target Name="_WslcCleanTlogs"
AfterTargets="WslcClean"
Condition="'@(WslcImage)' != '' AND '$(TLogLocation)' != ''">
<PropertyGroup>
<_WslcTlogDir>$([MSBuild]::EnsureTrailingSlash('$(TLogLocation)'))</_WslcTlogDir>
</PropertyGroup>
<Delete Files="$(_WslcTlogDir)wslc.%(WslcImage.Identity).read.1.tlog" />
<Delete Files="$(_WslcTlogDir)wslc.%(WslcImage.Identity).write.1.tlog" />
</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,21 @@

<Import Project="$(MSBuildThisFileDirectory)..\Microsoft.WSL.Containers.common.targets" />

<!-- ================================================================== -->
<!-- C# specific: CPS Up-to-Date Check for container source files -->
<!-- ================================================================== -->

<!-- Register build stamp and source files for CPS up-to-date check -->
<Target Name="_WslcRegisterUpToDateCheckInputs"
BeforeTargets="CollectUpToDateCheckBuiltDesignTime"
Condition="'$(UsingMicrosoftNETSdk)' == 'true' AND '@(WslcImage)' != ''">
<ItemGroup>
<UpToDateCheckBuilt Include="$(_WslcIntDir)wslc_%(WslcImage.Identity).built" />
<UpToDateCheckInput Include="%(WslcImage.Dockerfile)" Condition="'%(WslcImage.Dockerfile)' != ''" />
<_WslcUpToDateDirs Include="@(WslcImage->'%(Sources)')" Condition="'%(WslcImage.Sources)' != ''" />
<UpToDateCheckInput Include="%(_WslcUpToDateDirs.Identity)\**\*" Condition="'%(_WslcUpToDateDirs.Identity)' != ''" />
Comment thread
shuaiyuanxx marked this conversation as resolved.
<UpToDateCheckInput Include="%(WslcImage.Context)\**\*" Condition="'%(WslcImage.Sources)' == '' AND '%(WslcImage.Context)' != ''" />
</ItemGroup>
</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,100 @@ unset(_wslcsdk_arch)
unset(_wslcsdk_root)
unset(_wslcsdk_include_dir)
unset(_wslcsdk_lib_dir)

# ============================================================================
# Container Image Build Targets
# ============================================================================
#
# Provides the wslc_add_image() function for declaring container image
# build targets with incremental rebuild support.
#
# Usage:
# find_package(Microsoft.WSL.Containers REQUIRED)
#
# wslc_add_image(my-server
# IMAGE ghcr.io/myorg/my-server
# DOCKERFILE container/Dockerfile
# CONTEXT container/
# SOURCES container/src/*.cpp container/src/*.h
# TAG latest
# )
#
# add_dependencies(my_app my-server)
#
# The first positional argument is the CMake target name.
# IMAGE is the container image reference (required).

function(wslc_add_image _target_name)
cmake_parse_arguments(
PARSE_ARGV 1 ARG
"" # options (none)
"IMAGE;TAG;DOCKERFILE;CONTEXT" # one-value keywords
Comment thread
shuaiyuanxx marked this conversation as resolved.
"SOURCES" # multi-value keywords
)

# Validate required arguments
if(NOT ARG_IMAGE)
message(FATAL_ERROR "wslc_add_image: IMAGE is required")
endif()
# IMAGE must not contain a tag — use the TAG parameter instead.
# Allow ':' in the registry host portion (for example, localhost:5000/myimg)
# and only reject ':' when it appears after the last '/'.
string(FIND "${ARG_IMAGE}" "/" _last_slash_pos REVERSE)
string(FIND "${ARG_IMAGE}" ":" _last_colon_pos REVERSE)
Comment thread
shuaiyuanxx marked this conversation as resolved.
if(_last_colon_pos GREATER _last_slash_pos)
message(FATAL_ERROR "wslc_add_image: IMAGE '${ARG_IMAGE}' contains a tag. Specify the tag separately with the TAG parameter.")
endif()
if(NOT ARG_DOCKERFILE)
message(FATAL_ERROR "wslc_add_image: DOCKERFILE is required")
endif()
if(NOT ARG_CONTEXT)
message(FATAL_ERROR "wslc_add_image: CONTEXT is required")
endif()
Comment thread
shuaiyuanxx marked this conversation as resolved.

# Defaults
if(NOT ARG_TAG)
set(ARG_TAG "latest")
endif()

# Find wslc CLI
if(NOT WSLC_CLI_PATH)
find_program(WSLC_CLI_PATH wslc PATHS "$ENV{ProgramW6432}/WSL" "$ENV{ProgramFiles}/WSL")
if(NOT WSLC_CLI_PATH)
message(FATAL_ERROR "wslc CLI not found. Install WSL by running: wsl --install --no-distribution")
endif()
endif()

# Validate target name
string(REGEX MATCH "[^a-zA-Z0-9_.+-]" _bad_char "${_target_name}")
if(_bad_char)
message(FATAL_ERROR "wslc_add_image: '${_target_name}' contains unsupported character '${_bad_char}'. Supported characters are letters, digits, '_', '.', '+', and '-'.")
endif()

# Normalize paths to be independent of the build directory
get_filename_component(_dockerfile_path "${ARG_DOCKERFILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
get_filename_component(_context_path "${ARG_CONTEXT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")

Comment thread
shuaiyuanxx marked this conversation as resolved.
set(_image_ref "${ARG_IMAGE}:${ARG_TAG}")
Comment thread
shuaiyuanxx marked this conversation as resolved.
set(_stamp "${CMAKE_CURRENT_BINARY_DIR}/wslc_${_target_name}.built")

# Resolve source globs to file lists; default to CONTEXT contents if SOURCES omitted
if(ARG_SOURCES)
file(GLOB_RECURSE _resolved_sources CONFIGURE_DEPENDS ${ARG_SOURCES})
else()
file(GLOB_RECURSE _resolved_sources CONFIGURE_DEPENDS "${_context_path}/*")
Comment thread
shuaiyuanxx marked this conversation as resolved.
endif()

add_custom_command(
OUTPUT "${_stamp}"
COMMAND "${WSLC_CLI_PATH}" image build -t "${_image_ref}" -f "${_dockerfile_path}" "${_context_path}"
Comment thread
shuaiyuanxx marked this conversation as resolved.
COMMAND ${CMAKE_COMMAND} -E touch "${_stamp}"
DEPENDS ${_resolved_sources} "${_dockerfile_path}"
Comment thread
shuaiyuanxx marked this conversation as resolved.
COMMENT "WSLC: Building image '${_image_ref}'..."
VERBATIM
)

add_custom_target(${_target_name} ALL
DEPENDS "${_stamp}"
)
endfunction()