Skip to content
67 changes: 64 additions & 3 deletions src/windows/wslc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,72 @@ list(TRANSFORM WSLC_SUBDIR_PATHS APPEND /*.cpp OUTPUT_VARIABLE SOURCE_PATTERNS)
file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS ${HEADER_PATTERNS})
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS ${SOURCE_PATTERNS})

# Build Go template renderer library
set(GO_TEMPLATE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools/gotemplate)
set(GO_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated/gotemplate")

find_program(GO_EXECUTABLE go REQUIRED)

Comment thread
AmelBawa-msft marked this conversation as resolved.
# Reuse TARGET_PLATFORM already normalized by the root CMakeLists.txt
if("${TARGET_PLATFORM}" STREQUAL "arm64")
set(GO_ARCH arm64)
set(GO_LIB_MACHINE ARM64)
find_program(GO_CLANG_EXECUTABLE
NAMES clang
HINTS
"$ENV{VCToolsInstallDir}/Llvm/x64/bin"
"$ENV{VCINSTALLDIR}/Tools/Llvm/x64/bin"
"$ENV{ProgramFiles}/LLVM/bin"
"$ENV{ProgramFiles\(x86\)}/LLVM/bin")

if(NOT GO_CLANG_EXECUTABLE)
message(FATAL_ERROR "clang is required for arm64 cgo builds. Install the Windows Clang toolchain or add clang to PATH.")
endif()

set(GO_CGO_ENV
"CC=${GO_CLANG_EXECUTABLE}"
"CGO_CFLAGS=--target=aarch64-pc-windows-msvc"
"CGO_CXXFLAGS=--target=aarch64-pc-windows-msvc"
"CGO_LDFLAGS=--target=aarch64-pc-windows-msvc")
else()
set(GO_ARCH amd64)
set(GO_LIB_MACHINE X64)
set(GO_CGO_ENV)
Comment thread
AmelBawa-msft marked this conversation as resolved.
endif()

set(GO_OUTPUT_DLL ${GO_OUTPUT_DIR}/render.dll)
set(GO_OUTPUT_LIB ${GO_OUTPUT_DIR}/render.lib)
add_custom_command(
OUTPUT ${GO_OUTPUT_DLL} ${GO_OUTPUT_LIB}
COMMAND ${CMAKE_COMMAND} -E make_directory ${GO_OUTPUT_DIR}
COMMAND ${CMAKE_COMMAND} -E env GOOS=windows GOARCH=${GO_ARCH} CGO_ENABLED=1 ${GO_CGO_ENV}
${GO_EXECUTABLE} build -o ${GO_OUTPUT_DLL} -buildmode=c-shared -trimpath -ldflags=-s\ -w render.go
Comment thread
AmelBawa-msft marked this conversation as resolved.
COMMAND lib /def:${GO_TEMPLATE_DIR}/render.def /out:${GO_OUTPUT_LIB} /machine:${GO_LIB_MACHINE}
WORKING_DIRECTORY ${GO_TEMPLATE_DIR}
Comment thread
AmelBawa-msft marked this conversation as resolved.
DEPENDS ${GO_TEMPLATE_DIR}/render.go ${GO_TEMPLATE_DIR}/render.def
COMMENT "Building Go template renderer library"
VERBATIM
)

add_custom_target(gotemplate_lib ALL DEPENDS ${GO_OUTPUT_DLL} ${GO_OUTPUT_LIB})
Comment thread
AmelBawa-msft marked this conversation as resolved.

Comment thread
AmelBawa-msft marked this conversation as resolved.
Comment thread
AmelBawa-msft marked this conversation as resolved.
# Include Go source files for browsing in Solution Explorer
set(GO_SOURCES ${GO_TEMPLATE_DIR}/render.go)
set_source_files_properties(${GO_SOURCES} PROPERTIES HEADER_FILE_ONLY TRUE)

# Object library for WSLC components.
# Used to build the executable and also unit testing components.
add_library(wslclib OBJECT ${SOURCES} ${HEADERS})
add_library(wslclib OBJECT ${SOURCES} ${HEADERS} ${GO_SOURCES})
target_include_directories(wslclib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${WSLC_SUBDIR_PATHS})

target_link_libraries(wslclib
${COMMON_LINK_LIBRARIES}
yaml-cpp
common)
common
${GO_OUTPUT_LIB})

# Add dependency on Go library
add_dependencies(wslclib gotemplate_lib)

target_precompile_headers(wslclib REUSE_FROM common)
set_target_properties(wslclib PROPERTIES FOLDER windows)
Expand All @@ -24,7 +81,11 @@ add_executable(wslc $<TARGET_OBJECTS:wslclib>)

target_link_libraries(wslc wslclib)

add_custom_command(TARGET wslc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GO_OUTPUT_DLL} $<TARGET_FILE_DIR:wslc>/render.dll
)
Comment thread
AmelBawa-msft marked this conversation as resolved.

set_target_properties(wslc PROPERTIES FOLDER windows)

# For prettier source tree browsing
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES} ${HEADERS})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES} ${HEADERS} ${GO_SOURCES})
24 changes: 5 additions & 19 deletions src/windows/wslc/arguments/ArgumentValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ void Argument::Validate(const ArgMap& execArgs) const
{
switch (m_argType)
{
case ArgType::Format:
validation::ValidateFormatTypeFromString(execArgs.GetAll<ArgType::Format>(), m_name);
break;

case ArgType::Signal:
validation::ValidateWSLCSignalFromString(execArgs.GetAll<ArgType::Signal>(), m_name);
break;
Expand Down Expand Up @@ -144,29 +140,19 @@ WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring
return static_cast<WSLCSignal>(signalValue);
}

void ValidateFormatTypeFromString(const std::vector<std::wstring>& values, const std::wstring& argName)
{
for (const auto& value : values)
{
std::ignore = GetFormatTypeFromString(value, argName);
}
}

FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName)
FormatType GetFormatTypeFromString(const std::wstring& input)
{
if (IsEqual(input, L"json"))
{
return FormatType::Json;
}
else if (IsEqual(input, L"table"))

if (IsEqual(input, L"table"))
{
return FormatType::Table;
}
else
{
throw ArgumentException(std::format(
L"Invalid {} value: {} is not a recognized format type. Supported format types are: json, table.", argName, input));
}

return FormatType::Template;
Comment thread
AmelBawa-msft marked this conversation as resolved.
Comment thread
AmelBawa-msft marked this conversation as resolved.
}

} // namespace wsl::windows::wslc::validation
3 changes: 1 addition & 2 deletions src/windows/wslc/arguments/ArgumentValidation.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ T GetIntegerFromString(const std::wstring& value, const std::wstring& argName =
void ValidateWSLCSignalFromString(const std::vector<std::wstring>& values, const std::wstring& argName);
WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring& argName = {});

void ValidateFormatTypeFromString(const std::vector<std::wstring>& values, const std::wstring& argName);
FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName = {});
FormatType GetFormatTypeFromString(const std::wstring& input);

void ValidateVolumeMount(const std::vector<std::wstring>& values);

Expand Down
1 change: 0 additions & 1 deletion src/windows/wslc/commands/ContainerCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ struct ContainerListCommand final : public Command
std::wstring LongDescription() const override;

protected:
void ValidateArgumentsInternal(const ArgMap& execArgs) const override;
void ExecuteInternal(CLIExecutionContext& context) const override;
};

Expand Down
12 changes: 0 additions & 12 deletions src/windows/wslc/commands/ContainerListCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,6 @@ std::wstring ContainerListCommand::LongDescription() const
return Localization::WSLCCLI_ContainerListLongDesc();
}

void ContainerListCommand::ValidateArgumentsInternal(const ArgMap& execArgs) const
{
if (execArgs.Contains(ArgType::Format))
{
auto format = execArgs.Get<ArgType::Format>();
if (!IsEqual(format, L"json") && !IsEqual(format, L"table"))
{
throw CommandException(Localization::WSLCCLI_InvalidFormatError());
}
}
}

// clang-format off
void ContainerListCommand::ExecuteInternal(CLIExecutionContext& context) const
{
Expand Down
68 changes: 68 additions & 0 deletions src/windows/wslc/core/TemplateRenderer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

TemplateRenderer.cpp

Abstract:

Implementation of the Go template renderer.

Comment thread
AmelBawa-msft marked this conversation as resolved.
--*/

#include "TemplateRenderer.h"
#include <string>

// Forward-declare the Go template renderer functions (exported from render.dll).
// We declare these directly instead of including the cgo-generated render.h
// to avoid Go boilerplate types that don't compile cleanly with MSVC.
extern "C" {
int TryRenderGoTemplate(const char* templateStr, const char* jsonData, char** output);
void FreeGoString(char* ptr);
}

namespace wsl::windows::wslc::core {

using namespace wsl::shared::string;

TemplateRenderer::RenderResult TemplateRenderer::TryRender(const std::string& templateStr, const std::string& jsonData, std::wstring& output)
{
try
{
char* rawOutput = nullptr;
auto success = TryRenderGoTemplate(templateStr.c_str(), jsonData.c_str(), &rawOutput);

Comment thread
AmelBawa-msft marked this conversation as resolved.
std::string result(rawOutput ? rawOutput : "");
FreeGoString(rawOutput);

output = MultiByteToWide(result);
return static_cast<RenderResult>(success);
}
catch (const std::exception& ex)
{
output = MultiByteToWide(ex.what());
return RenderResult::Fail_Unknown;
}
}

void TemplateRenderer::Render(const std::string& templateStr, const std::string& jsonData, std::wstring& output)
{
switch (TryRender(templateStr, jsonData, output))
{
case RenderResult::Success:
return;
case RenderResult::Fail_NullPointer:
THROW_HR(E_POINTER);
case RenderResult::Fail_ParseJSON:
case RenderResult::Fail_ParseTemplate:
case RenderResult::Fail_ExecuteTemplate:
THROW_HR_WITH_USER_ERROR(E_INVALIDARG, output);
case RenderResult::Fail_Unknown:
default:
THROW_HR(E_UNEXPECTED);
}
}

} // namespace wsl::windows::wslc::core
39 changes: 39 additions & 0 deletions src/windows/wslc/core/TemplateRenderer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

TemplateRenderer.h

Abstract:

This file contains the interface for rendering Go templates with JSON data.

--*/

#pragma once

#include <string>

namespace wsl::windows::wslc::core {

struct TemplateRenderer
{
enum class RenderResult
{
Success = 0,
Fail_NullPointer = 1,
Fail_ParseJSON = 2,
Fail_ParseTemplate = 3,
Fail_ExecuteTemplate = 4,

// All other failures
Fail_Unknown = -1,
};

static RenderResult TryRender(const std::string& templateStr, const std::string& jsonData, std::wstring& output);
static void Render(const std::string& templateStr, const std::string& jsonData, std::wstring& output);
};

} // namespace wsl::windows::wslc::core
1 change: 1 addition & 0 deletions src/windows/wslc/services/ContainerModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum class FormatType
{
Table,
Json,
Template,
};
Comment thread
AmelBawa-msft marked this conversation as resolved.

struct ContainerOptions
Expand Down
15 changes: 15 additions & 0 deletions src/windows/wslc/tasks/ContainerTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Module Name:
Implementation of container command related execution logic.

--*/

#include "Argument.h"
#include "ArgumentValidation.h"
#include "CLIExecutionContext.h"
Expand All @@ -20,6 +21,7 @@ Module Name:
#include "SessionModel.h"
#include "SessionService.h"
#include "TableOutput.h"
#include "TemplateRenderer.h"
#include <wil/result_macros.h>
#include <wslc_schema.h>

Expand All @@ -29,6 +31,7 @@ using namespace wsl::windows::common::wslutil;
using namespace wsl::windows::wslc::execution;
using namespace wsl::windows::wslc::models;
using namespace wsl::windows::wslc::services;
using namespace wsl::windows::wslc::core;

namespace wsl::windows::wslc::task {
void AttachContainer::operator()(CLIExecutionContext& context) const
Expand Down Expand Up @@ -167,6 +170,18 @@ void ListContainers(CLIExecutionContext& context)
table.Complete();
break;
}
case FormatType::Template:
{
auto templateStr = WideToMultiByte(context.Args.Get<ArgType::Format>());
for (const auto& container : containers)
{
auto json = ToJson(container);
std::wstring result;
TemplateRenderer::Render(templateStr, json, result);
PrintMessage(result);
}
Comment thread
AmelBawa-msft marked this conversation as resolved.
break;
}
default:
THROW_HR(E_UNEXPECTED);
}
Expand Down
14 changes: 14 additions & 0 deletions src/windows/wslc/tasks/ImageTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ Module Name:
#include "PullImageCallback.h"
#include "TableOutput.h"
#include "Task.h"
#include "TemplateRenderer.h"
#include <format>

using namespace wsl::shared;
using namespace wsl::windows::common::string;
using namespace wsl::windows::common::wslutil;
using namespace wsl::windows::wslc::execution;
using namespace wsl::windows::wslc::services;
using namespace wsl::windows::wslc::core;

namespace wsl::windows::wslc::task {
void BuildImage(CLIExecutionContext& context)
Expand Down Expand Up @@ -124,6 +126,18 @@ void ListImages(CLIExecutionContext& context)
table.Complete();
break;
}
case FormatType::Template:
{
auto templateStr = WideToMultiByte(context.Args.Get<ArgType::Format>());
for (const auto& image : images)
{
auto json = ToJson(image);
std::wstring result;
TemplateRenderer::Render(templateStr, json, result);
PrintMessage(result);
}
Comment thread
AmelBawa-msft marked this conversation as resolved.
break;
}
default:
THROW_HR(E_UNEXPECTED);
}
Expand Down
5 changes: 5 additions & 0 deletions src/windows/wslc/tools/gotemplate/render.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LIBRARY render

EXPORTS
TryRenderGoTemplate
FreeGoString
Comment thread
AmelBawa-msft marked this conversation as resolved.
Loading
Loading