VS: Add support for XAML source files

XAML files are by default of type Page in the vcxproj and can be
overriden by setting the VS_XAML_TYPE property.  The .cpp and .h file
of the same name are automatically added as depending on the XAML file.

New VSXaml test builds a basic XAML WindowsStore 8.1 app with VS2013.
This commit is contained in:
Gilles Khouzam 2015-03-31 13:49:39 -07:00 committed by Brad King
parent 84136c5a83
commit 01a9ab0def
24 changed files with 498 additions and 4 deletions

View File

@ -298,6 +298,7 @@ Properties on Source Files
/prop_sf/VS_SHADER_FLAGS
/prop_sf/VS_SHADER_MODEL
/prop_sf/VS_SHADER_TYPE
/prop_sf/VS_XAML_TYPE
/prop_sf/WRAP_EXCLUDE
/prop_sf/XCODE_EXPLICIT_FILE_TYPE
/prop_sf/XCODE_LAST_KNOWN_FILE_TYPE

View File

@ -0,0 +1,6 @@
VS_XAML_TYPE
------------
Mark a XAML source file as a different type than the default ``Page``.
The most common usage would be to set the default App.xaml file as
ApplicationDefinition.

View File

@ -0,0 +1,6 @@
vs-xaml
-------
* The :ref:`Visual Studio Generators` learned to support ``.xaml``
source files and automatically associate them with corresponding
``.h`` and ``.cpp`` sources.

View File

@ -56,6 +56,7 @@ struct ResxTag {};
struct ModuleDefinitionFileTag {};
struct AppManifestTag{};
struct CertificatesTag{};
struct XamlTag{};
template<typename Tag, typename OtherTag>
struct IsSameTag
@ -98,6 +99,20 @@ struct DoAccept<true>
data.ExpectedResxHeaders.insert(hFileName);
data.ResxSources.push_back(f);
}
static void Do(cmGeneratorTarget::XamlData& data, cmSourceFile* f)
{
// Build and save the name of the corresponding .h and .cpp file
// This relationship will be used later when building the project files.
// Both names would have been auto generated from Visual Studio
// where the user supplied the file name and Visual Studio
// appended the suffix.
std::string xaml = f->GetFullPath();
std::string hFileName = xaml + ".h";
std::string cppFileName = xaml + ".cpp";
data.ExpectedXamlHeaders.insert(hFileName);
data.ExpectedXamlSources.insert(cppFileName);
data.XamlSources.push_back(f);
}
static void Do(std::string& data, cmSourceFile* f)
{
data = f->GetFullPath();
@ -186,6 +201,10 @@ struct TagVisitor
{
DoAccept<IsSameTag<Tag, CertificatesTag>::Result>::Do(this->Data, sf);
}
else if (ext == "xaml")
{
DoAccept<IsSameTag<Tag, XamlTag>::Result>::Do(this->Data, sf);
}
else if(this->Header.find(sf->GetFullPath().c_str()))
{
DoAccept<IsSameTag<Tag, HeaderSourcesTag>::Result>::Do(this->Data, sf);
@ -437,6 +456,36 @@ cmGeneratorTarget
IMPLEMENT_VISIT(Certificates);
}
//----------------------------------------------------------------------------
void
cmGeneratorTarget::GetExpectedXamlHeaders(std::set<std::string>& headers,
const std::string& config) const
{
XamlData data;
IMPLEMENT_VISIT_IMPL(Xaml, COMMA cmGeneratorTarget::XamlData)
headers = data.ExpectedXamlHeaders;
}
//----------------------------------------------------------------------------
void
cmGeneratorTarget::GetExpectedXamlSources(std::set<std::string>& srcs,
const std::string& config) const
{
XamlData data;
IMPLEMENT_VISIT_IMPL(Xaml, COMMA cmGeneratorTarget::XamlData)
srcs = data.ExpectedXamlSources;
}
//----------------------------------------------------------------------------
void cmGeneratorTarget
::GetXamlSources(std::vector<cmSourceFile const*>& srcs,
const std::string& config) const
{
XamlData data;
IMPLEMENT_VISIT_IMPL(Xaml, COMMA cmGeneratorTarget::XamlData)
srcs = data.XamlSources;
}
//----------------------------------------------------------------------------
bool cmGeneratorTarget::IsSystemIncludeDirectory(const std::string& dir,
const std::string& config) const

View File

@ -58,6 +58,12 @@ public:
const std::string& config) const;
void GetCertificates(std::vector<cmSourceFile const*>&,
const std::string& config) const;
void GetXamlSources(std::vector<cmSourceFile const*>&,
const std::string& config) const;
void GetExpectedXamlHeaders(std::set<std::string>&,
const std::string& config) const;
void GetExpectedXamlSources(std::set<std::string>&,
const std::string& config) const;
void ComputeObjectMapping();
@ -132,6 +138,13 @@ public:
mutable std::set<std::string> ExpectedResxHeaders;
mutable std::vector<cmSourceFile const*> ResxSources;
};
struct XamlData {
std::set<std::string> ExpectedXamlHeaders;
std::set<std::string> ExpectedXamlSources;
std::vector<cmSourceFile const*> XamlSources;
};
private:
friend class cmTargetTraceDependencies;
struct SourceEntry { std::vector<cmSourceFile*> Depends; };

View File

@ -461,6 +461,7 @@ void cmVisualStudio10TargetGenerator::Generate()
this->WriteAllSources();
this->WriteDotNetReferences();
this->WriteEmbeddedResourceGroup();
this->WriteXamlFilesGroup();
this->WriteWinRTReferences();
this->WriteProjectReferences();
this->WriteString(
@ -522,8 +523,7 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup()
this->WriteString("<DependentUpon>", 3);
std::string hFileName = obj.substr(0, obj.find_last_of(".")) + ".h";
(*this->BuildFileStream ) << hFileName;
this->WriteString("</DependentUpon>\n", 3);
(*this->BuildFileStream) << hFileName << "</DependentUpon>\n";
std::vector<std::string> const * configs =
this->GlobalGenerator->GetConfigurations();
@ -546,6 +546,38 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup()
}
}
void cmVisualStudio10TargetGenerator::WriteXamlFilesGroup()
{
std::vector<cmSourceFile const*> xamlObjs;
this->GeneratorTarget->GetXamlSources(xamlObjs, "");
if (!xamlObjs.empty())
{
this->WriteString("<ItemGroup>\n", 1);
for (std::vector<cmSourceFile const*>::const_iterator
oi = xamlObjs.begin(); oi != xamlObjs.end(); ++oi)
{
std::string obj = (*oi)->GetFullPath();
std::string xamlType;
const char * xamlTypeProperty = (*oi)->GetProperty("VS_XAML_TYPE");
if (xamlTypeProperty)
{
xamlType = xamlTypeProperty;
}
else
{
xamlType = "Page";
}
this->WriteSource(xamlType, *oi, ">\n");
this->WriteString("<SubType>Designer</SubType>\n", 3);
this->WriteString("</", 2);
(*this->BuildFileStream) << xamlType << ">\n";
}
this->WriteString("</ItemGroup>\n", 1);
}
}
void cmVisualStudio10TargetGenerator::WriteTargetSpecificReferences()
{
if(this->MSTools)
@ -1192,12 +1224,21 @@ WriteGroupSources(const char* name,
void cmVisualStudio10TargetGenerator::WriteHeaderSource(cmSourceFile const* sf)
{
if(this->IsResxHeader(sf->GetFullPath()))
std::string const& fileName = sf->GetFullPath();
if (this->IsResxHeader(fileName))
{
this->WriteSource("ClInclude", sf, ">\n");
this->WriteString("<FileType>CppForm</FileType>\n", 3);
this->WriteString("</ClInclude>\n", 2);
}
else if (this->IsXamlHeader(fileName))
{
this->WriteSource("ClInclude", sf, ">\n");
this->WriteString("<DependentUpon>", 3);
std::string xamlFileName = fileName.substr(0, fileName.find_last_of("."));
(*this->BuildFileStream) << xamlFileName << "</DependentUpon>\n";
this->WriteString("</ClInclude>\n", 2);
}
else
{
this->WriteSource("ClInclude", sf);
@ -1669,6 +1710,17 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
" ", "\n", lang);
}
}
if (this->IsXamlSource(source->GetFullPath()))
{
(*this->BuildFileStream) << firstString;
firstString = ""; // only do firstString once
hasFlags = true;
this->WriteString("<DependentUpon>", 3);
const std::string& fileName = source->GetFullPath();
std::string xamlFileName = fileName.substr(0, fileName.find_last_of("."));
(*this->BuildFileStream) << xamlFileName << "</DependentUpon>\n";
}
return hasFlags;
}
@ -2749,6 +2801,28 @@ bool cmVisualStudio10TargetGenerator::
return it != expectedResxHeaders.end();
}
bool cmVisualStudio10TargetGenerator::
IsXamlHeader(const std::string& headerFile)
{
std::set<std::string> expectedXamlHeaders;
this->GeneratorTarget->GetExpectedXamlHeaders(expectedXamlHeaders, "");
std::set<std::string>::const_iterator it =
expectedXamlHeaders.find(headerFile);
return it != expectedXamlHeaders.end();
}
bool cmVisualStudio10TargetGenerator::
IsXamlSource(const std::string& sourceFile)
{
std::set<std::string> expectedXamlSources;
this->GeneratorTarget->GetExpectedXamlSources(expectedXamlSources, "");
std::set<std::string>::const_iterator it =
expectedXamlSources.find(sourceFile);
return it != expectedXamlSources.end();
}
void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings()
{
bool isAppContainer = false;

View File

@ -69,6 +69,7 @@ private:
void WriteEmbeddedResourceGroup();
void WriteWinRTReferences();
void WriteWinRTPackageCertificateKeyFile();
void WriteXamlFilesGroup();
void WritePathAndIncrementalLinkOptions();
void WriteItemDefinitionGroups();
void VerifyNecessaryFiles();
@ -119,6 +120,8 @@ private:
void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed,
const std::vector<cmSourceGroup>& allGroups);
bool IsResxHeader(const std::string& headerFile);
bool IsXamlHeader(const std::string& headerFile);
bool IsXamlSource(const std::string& headerFile);
cmIDEFlagTable const* GetClFlagTable() const;
cmIDEFlagTable const* GetRcFlagTable() const;

View File

@ -1927,6 +1927,17 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
add_test_VSWinStorePhone(vs12-store81-X86 "Visual Studio 12 2013" WindowsStore 8.1)
add_test_VSWinStorePhone(vs12-store81-ARM "Visual Studio 12 2013 ARM" WindowsStore 8.1)
add_test_VSWinStorePhone(vs12-store81-X64 "Visual Studio 12 2013 Win64" WindowsStore 8.1)
add_test(NAME VSXaml COMMAND ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/VSXaml"
"${CMake_BINARY_DIR}/Tests/VSXaml"
--build-generator "Visual Studio 12 2013"
--build-project VSXaml
--build-config $<CONFIGURATION>
--build-options -DCMAKE_SYSTEM_NAME=WindowsStore
-DCMAKE_SYSTEM_VERSION=8.1
)
endif()
if(vs11 AND wp80)
add_test_VSWinStorePhone(vs11-phone80-X86 "Visual Studio 11 2012" WindowsPhone 8.0)

View File

@ -14,7 +14,7 @@ include(CheckCXXSourceCompiles)
include(CheckIncludeFile)
# Note: The designable form is assumed to have a .h extension as is default in Visual Studio.
# Node: The designable form is assumed to have a .resx file with the same name and path (save extension) as is default in Visual Studio
# Note: The designable form is assumed to have a .resx file with the same name and path (save extension) as is default in Visual Studio
set(TARGET_H
WindowsFormsResx/MyForm.h

7
Tests/VSXaml/App.xaml Normal file
View File

@ -0,0 +1,7 @@
<Application
x:Class="VSXaml.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:VSXaml">
</Application>

125
Tests/VSXaml/App.xaml.cpp Normal file
View File

@ -0,0 +1,125 @@
//
// App.xaml.cpp
// Implementation of the App class.
//
#include "pch.h"
#include "MainPage.xaml.h"
using namespace VSXaml;
using namespace Platform;
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
App::App()
{
InitializeComponent();
Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e)
{
#if _DEBUG
// Show graphics profiling information while debugging.
if (IsDebuggerPresent())
{
// Display the current frame rate counters
DebugSettings->EnableFrameRateCounter = true;
}
#endif
auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == nullptr)
{
// Create a Frame to act as the navigation context and associate it with
// a SuspensionManager key
rootFrame = ref new Frame();
// Set the default language
rootFrame->Language = Windows::Globalization::ApplicationLanguages::Languages->GetAt(0);
rootFrame->NavigationFailed += ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(this, &App::OnNavigationFailed);
if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
{
// TODO: Restore the saved session state only when appropriate, scheduling the
// final launch steps after the restore is complete
}
if (rootFrame->Content == nullptr)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments);
}
// Place the frame in the current Window
Window::Current->Content = rootFrame;
// Ensure the current window is active
Window::Current->Activate();
}
else
{
if (rootFrame->Content == nullptr)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments);
}
// Ensure the current window is active
Window::Current->Activate();
}
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e)
{
(void) sender; // Unused parameter
(void) e; // Unused parameter
//TODO: Save application state and stop any background activity
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e)
{
throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name);
}

27
Tests/VSXaml/App.xaml.h Normal file
View File

@ -0,0 +1,27 @@
//
// App.xaml.h
// Declaration of the App class.
//
#pragma once
#include "App.g.h"
namespace VSXaml
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
ref class App sealed
{
protected:
virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override;
internal:
App();
private:
void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e);
void OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e);
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

View File

@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.2)
project(VSXaml)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(SOURCE_FILES
App.xaml.cpp
MainPage.xaml.cpp
pch.cpp
)
set(HEADER_FILES
App.xaml.h
MainPage.xaml.h
pch.h
)
set(XAML_FILES
App.xaml
MainPage.xaml
)
set(ASSET_FILES
Assets/Logo.scale-100.png
Assets/SmallLogo.scale-100.png
Assets/SplashScreen.scale-100.png
Assets/StoreLogo.scale-100.png
)
set(CONTENT_FILES
Package.appxmanifest
)
set(RESOURCE_FILES
${CONTENT_FILES} ${ASSET_FILES}
VSXaml_TemporaryKey.pfx)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set_property(SOURCE ${CONTENT_FILES} PROPERTY VS_DEPLOYMENT_CONTENT 1)
set_property(SOURCE ${ASSET_FILES} PROPERTY VS_DEPLOYMENT_CONTENT 1)
set_property(SOURCE ${ASSET_FILES} PROPERTY VS_DEPLOYMENT_LOCATION "Assets")
set_property(SOURCE "App.xaml" PROPERTY VS_XAML_TYPE "ApplicationDefinition")
source_group("Source Files" FILES ${SOURCE_FILES})
source_group("Header Files" FILES ${HEADER_FILES})
source_group("Resource Files" FILES ${RESOURCE_FILES})
source_group("Xaml Files" FILES ${XAML_FILES})
add_executable(VSXaml WIN32 ${SOURCE_FILES} ${HEADER_FILES} ${RESOURCE_FILES} ${XAML_FILES})
set_property(TARGET VSXaml PROPERTY VS_WINRT_COMPONENT TRUE)

View File

@ -0,0 +1,14 @@
<Page
x:Class="VSXaml.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:VSXaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="I'm a CMake XAML App" HorizontalAlignment="Center" VerticalAlignment="Center"
Style="{StaticResource HeaderTextBlockStyle}"/>
</Grid>
</Page>

View File

@ -0,0 +1,27 @@
//
// MainPage.xaml.cpp
// Implementation of the MainPage class.
//
#include "pch.h"
#include "MainPage.xaml.h"
using namespace VSXaml;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
MainPage::MainPage()
{
InitializeComponent();
}

View File

@ -0,0 +1,21 @@
//
// MainPage.xaml.h
// Declaration of the MainPage class.
//
#pragma once
#include "MainPage.g.h"
namespace VSXaml
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public ref class MainPage sealed
{
public:
MainPage();
};
}

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
<Identity Name="19ff96f1-8379-4e14-8b9d-04648b3b36a9"
Publisher="CN=Microsoft"
Version="1.0.0.0" />
<Properties>
<DisplayName>VSXaml</DisplayName>
<PublisherDisplayName>Microsoft</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Prerequisites>
<OSMinVersion>6.3.0</OSMinVersion>
<OSMaxVersionTested>6.3.0</OSMaxVersionTested>
</Prerequisites>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="VSXaml.App">
<m2:VisualElements
DisplayName="VSXaml"
Square150x150Logo="Assets\Logo.png"
Square30x30Logo="Assets\SmallLogo.png"
Description="VSXaml"
ForegroundText="light"
BackgroundColor="#464646">
<m2:SplashScreen Image="Assets\SplashScreen.png" />
</m2:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
</Capabilities>
</Package>

Binary file not shown.

6
Tests/VSXaml/pch.cpp Normal file
View File

@ -0,0 +1,6 @@
//
// pch.cpp
// Include the standard header and generate the precompiled header.
//
#include "pch.h"

11
Tests/VSXaml/pch.h Normal file
View File

@ -0,0 +1,11 @@
//
// pch.h
// Header for standard system include files.
//
#pragma once
#include <collection.h>
#include <ppltasks.h>
#include "App.xaml.h"