Commit fce6233d authored by Brad King's avatar Brad King
Browse files

Merge branch 'csproj_add_free_source_tags' into release

parents baed3810 506207f9
......@@ -66,6 +66,7 @@ All Modules
/module/CPackRPM
/module/CPack
/module/CPackWIX
/module/CSharpUtilities
/module/CTest
/module/CTestCoverageCollectGCOV
/module/CTestScriptMode
......
......@@ -366,6 +366,7 @@ Properties on Source Files
/prop_sf/SKIP_AUTOUIC
/prop_sf/SYMBOLIC
/prop_sf/VS_COPY_TO_OUT_DIR
/prop_sf/VS_CSHARP_tagname
/prop_sf/VS_DEPLOYMENT_CONTENT
/prop_sf/VS_DEPLOYMENT_LOCATION
/prop_sf/VS_INCLUDE_IN_VSIX
......
.. cmake-module:: ../../Modules/CSharpUtilities.cmake
VS_CSHARP_<tagname>
-------------------
Visual Studio and CSharp source-file-specific configuration.
Tell the Visual Studio generator to set the source file tag
``<tagname>`` to a given value in the generated Visual Studio CSharp
project. Ignored on other generators and languages. This property
can be used to define dependencies between source files or set any
other Visual Studio specific parameters.
Example usage:
.. code-block:: cmake
set_source_files_property(<filename>
PROPERTIES
VS_CSHARP_DependentUpon <other file>
VS_CSHARP_SubType "Form")
......@@ -34,15 +34,6 @@ C#
Visual Studio (``VS_*``) are worth a look (for setting toolset
versions, root namespaces, assembly icons, ...).
* Auto-linking in ``.csproj`` files: In C#/.NET development with
Visual Studio there are a number of visual editors used which
generate code. Both the generated files and the ones edited
with the UI are connected in the ``.csproj`` file using
``<DependentUpon>`` tags. If CMake finds within a C# project
any source file with extension ``.Designer.cs`` or ``.xaml.cs``,
it checks sibling files with extension ``.xaml``, ``.settings``,
``.resx`` or ``.cs`` and establishes the dependency connection.
CUDA
^^^^
......@@ -229,6 +220,11 @@ Properties
Modules
-------
* A :module:`CSharpUtilities` module was added to aid parameterization of
Visual Studio C# targets. It provides functions to allow automated
setting of source file properties to support Windows Forms, WPF/XAML or
other technologies as needed.
* The :module:`ExternalData` module learned to support multiple
content links for one data file using different hashes, e.g.
``img.png.sha256`` and ``img.png.sha1``. This allows objects
......
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
CSharpUtilities
---------------
Functions to make configuration of CSharp/.NET targets easier.
A collection of CMake utility functions useful for dealing with CSharp
targets for Visual Studio generators from version 2010 and later.
The following functions are provided by this module:
**Main functions**
- :command:`csharp_set_windows_forms_properties`
- :command:`csharp_set_designer_cs_properties`
- :command:`csharp_set_xaml_cs_properties`
**Helper functions**
- :command:`csharp_get_filename_keys`
- :command:`csharp_get_filename_key_base`
- :command:`csharp_get_dependentupon_name`
Main functions provided by the module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. command:: csharp_set_windows_forms_properties
Sets source file properties for use of Windows Forms. Use this, if your CSharp
target uses windows forms::
csharp_set_windows_forms_properties([<file1> [<file2> [...]]])
``<fileN>``
List of all source files which are relevant for setting the
:prop_sf:`VS_CSHARP_<tagname>` properties (including ``.cs``, ``.resx`` and
``.Designer.cs`` extensions).
In the list of all given files for all files ending with ``.Designer.cs`` and
``.resx`` is searched. For every *designer* or *resource* file a file with the
same base name but only ``.cs`` as extension is searched. If this is found, the
:prop_sf:`VS_CSHARP_<tagname>` properties are set as follows:
for the **.cs** file:
- VS_CSHARP_SubType "Form"
for the **.Designer.cs** file (if it exists):
- VS_CSHARP_DependentUpon <cs-filename>
- VS_CSHARP_DesignTime "" (delete tag if previously defined)
- VS_CSHARP_AutoGen ""(delete tag if previously defined)
for the **.resx** file (if it exists):
- VS_RESOURCE_GENERATOR "" (delete tag if previously defined)
- VS_CSHARP_DependentUpon <cs-filename>
- VS_CSHARP_SubType "Designer"
.. command:: csharp_set_designer_cs_properties
Sets source file properties for use of WPF/XAML. Use this, if your CSharp
target uses WPF/XAML::
csharp_set_designer_cs_properties([<file1> [<file2> [...]]])
``<fileN>``
List of all source files which are relevant for setting the
:prop_sf:`VS_CSHARP_<tagname>` properties (including ``.cs``,
``.resx``, ``.settings`` and ``.Designer.cs`` extensions).
In the list of all given files for all files ending with
``.Designer.cs`` is searched. For every *designer* file all files
with the same base name but different extensions are searched. If
a match is found, the source file properties of the *designer* file
are set depending on the extension of the matched file:
if match is **.resx** file:
- VS_CSHARP_AutoGen "True"
- VS_CSHARP_DesignTime "True"
- VS_CSHARP_DependentUpon <resx-filename>
if match is **.cs** file:
- VS_CSHARP_DependentUpon <cs-filename>
if match is **.settings** file:
- VS_CSHARP_AutoGen "True"
- VS_CSHARP_DesignTimeSharedInput "True"
- VS_CSHARP_DependentUpon <settings-filename>
.. command:: csharp_set_xaml_cs_properties
Sets source file properties for use of WPF/XAML. Use this, if your
CSharp target uses WPF/XAML::
csharp_set_xaml_cs_properties([<file1> [<file2> [...]]])
``<fileN>``
List of all source files which are relevant for setting the
:prop_sf:`VS_CSHARP_<tagname>` properties (including ``.cs``,
``.xaml``, and ``.xaml.cs`` extensions).
In the list of all given files for all files ending with
``.xaml.cs`` is searched. For every xaml file, a file
with the same base name but extension ``.xaml`` is searched.
If a match is found, the source file properties of the ``.xaml.cs``
file are set:
- VS_CSHARP_DependentUpon <xaml-filename>
Helper functions which are used by the above ones
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. command:: csharp_get_filename_keys
Helper function which computes a list of key values to identify
source files independently of relative/absolute paths given in cmake
and eliminates case sensitivity::
csharp_get_filename_keys(OUT [<file1> [<file2> [...]]])
``OUT``
name of the variable in which the list of keys is stored
``<fileN>``
filename as given to to CSharp target using :command:`add_library`
or :command:`add_executable`
In some way the function applies a canonicalization to the source names.
This is necessary to find file matches if the files have been added to
the target with different directory prefixes:
.. code-block:: cmake
add_library(lib
myfile.cs
${CMAKE_CURRENT_SOURCE_DIR}/myfile.Designer.cs)
set_source_files_properties(myfile.Designer.cs PROPERTIES
VS_CSHARP_DependentUpon myfile.cs)
# this will fail, because in cmake
# - ${CMAKE_CURRENT_SOURCE_DIR}/myfile.Designer.cs
# - myfile.Designer.cs
# are not the same source file. The source file property is not set.
.. command:: csharp_get_filename_key_base
Returns the full filepath and name **withouth** extension of a key.
KEY is expected to be a key from csharp_get_filename_keys. In BASE
the value of KEY without the file extension is returned::
csharp_get_filename_key_base(BASE KEY)
``BASE``
The computed "base" of ``KEY``.
``KEY``
The key of which the base will be computed. Expected to be a
upper case full filename.
.. command:: csharp_get_dependentupon_name
Computes a string which can be used as value for the source file property
:prop_sf:`VS_CSHARP_<tagname>` with *target* being ``DependentUpon``::
csharp_get_dependentupon_name(NAME FILE)
``NAME``
result value
``FILE``
filename to convert to DependentUpon value
Actually this is only the filename without any path given at the moment.
#]=======================================================================]
function(csharp_get_filename_keys OUT)
set(${OUT} "")
foreach(f ${ARGN})
get_filename_component(f ${f} REALPATH)
string(TOUPPER ${f} f)
list(APPEND ${OUT} ${f})
endforeach()
set(${OUT} "${${OUT}}" PARENT_SCOPE)
endfunction()
function(csharp_get_filename_key_base base key)
get_filename_component(dir ${key} DIRECTORY)
get_filename_component(fil ${key} NAME_WE)
set(${base} "${dir}/${fil}" PARENT_SCOPE)
endfunction()
function(csharp_get_dependentupon_name out in)
get_filename_component(${out} ${in} NAME)
set(${out} ${${out}} PARENT_SCOPE)
endfunction()
function(csharp_set_windows_forms_properties)
csharp_get_filename_keys(fileKeys ${ARGN})
foreach(key ${fileKeys})
get_filename_component(ext ${key} EXT)
if(${ext} STREQUAL ".DESIGNER.CS" OR
${ext} STREQUAL ".RESX")
csharp_get_filename_key_base(NAME_BASE ${key})
list(FIND fileKeys "${NAME_BASE}.CS" FILE_INDEX)
if(NOT ${FILE_INDEX} EQUAL -1)
list(GET ARGN ${FILE_INDEX} FILE_NAME)
# set properties of main form file
set_source_files_properties("${FILE_NAME}"
PROPERTIES
VS_CSHARP_SubType "Form")
csharp_get_dependentupon_name(LINK "${FILE_NAME}")
# set properties of designer file (if found)
list(FIND fileKeys "${NAME_BASE}.DESIGNER.CS" FILE_INDEX)
if(NOT ${FILE_INDEX} EQUAL -1)
list(GET ARGN ${FILE_INDEX} FILE_NAME)
set_source_files_properties("${FILE_NAME}"
PROPERTIES
VS_CSHARP_DependentUpon "${LINK}"
VS_CSHARP_DesignTime ""
VS_CSHARP_AutoGen "")
endif()
# set properties of corresponding resource file (if found)
list(FIND fileKeys "${NAME_BASE}.RESX" FILE_INDEX)
if(NOT ${FILE_INDEX} EQUAL -1)
list(GET ARGN ${FILE_INDEX} FILE_NAME)
set_source_files_properties("${FILE_NAME}"
PROPERTIES
VS_RESOURCE_GENERATOR ""
VS_CSHARP_DependentUpon "${LINK}"
VS_CSHARP_SubType "Designer")
endif()
endif()
endif()
endforeach()
endfunction()
function(csharp_set_designer_cs_properties)
csharp_get_filename_keys(fileKeys ${ARGN})
set(INDEX -1)
foreach(key ${fileKeys})
math(EXPR INDEX "${INDEX}+1")
list(GET ARGN ${INDEX} source)
get_filename_component(ext ${key} EXT)
if(${ext} STREQUAL ".DESIGNER.CS")
csharp_get_filename_key_base(NAME_BASE ${key})
if("${NAME_BASE}.RESX" IN_LIST fileKeys)
list(FIND fileKeys "${NAME_BASE}.RESX" FILE_INDEX)
list(GET ARGN ${FILE_INDEX} FILE_NAME)
csharp_get_dependentupon_name(LINK "${FILE_NAME}")
set_source_files_properties("${source}"
PROPERTIES
VS_CSHARP_AutoGen "True"
VS_CSHARP_DesignTime "True"
VS_CSHARP_DependentUpon "${LINK}")
elseif("${NAME_BASE}.CS" IN_LIST fileKeys)
list(FIND fileKeys "${NAME_BASE}.CS" FILE_INDEX)
list(GET ARGN ${FILE_INDEX} FILE_NAME)
csharp_get_dependentupon_name(LINK "${FILE_NAME}")
set_source_files_properties("${source}"
PROPERTIES
VS_CSHARP_DependentUpon "${LINK}")
elseif("${NAME_BASE}.SETTINGS" IN_LIST fileKeys)
list(FIND fileKeys "${NAME_BASE}.SETTINGS" FILE_INDEX)
list(GET ARGN ${FILE_INDEX} FILE_NAME)
csharp_get_dependentupon_name(LINK "${FILE_NAME}")
set_source_files_properties("${source}"
PROPERTIES
VS_CSHARP_AutoGen "True"
VS_CSHARP_DesignTimeSharedInput "True"
VS_CSHARP_DependentUpon "${LINK}")
endif()
endif()
endforeach()
endfunction()
function(csharp_set_xaml_cs_properties)
csharp_get_filename_keys(fileKeys ${ARGN})
set(INDEX -1)
foreach(key ${fileKeys})
math(EXPR INDEX "${INDEX}+1")
list(GET ARGN ${INDEX} source)
get_filename_component(ext ${key} EXT)
if(${ext} STREQUAL ".XAML.CS")
csharp_get_filename_key_base(NAME_BASE ${key})
if("${NAME_BASE}.XAML" IN_LIST fileKeys)
list(FIND fileKeys "${NAME_BASE}.XAML" FILE_INDEX)
list(GET ARGN ${FILE_INDEX} FILE_NAME)
csharp_get_dependentupon_name(LINK "${FILE_NAME}")
set_source_files_properties("${source}"
PROPERTIES
VS_CSHARP_DependentUpon "${LINK}")
endif()
endif()
endforeach()
endfunction()
......@@ -86,6 +86,7 @@ public:
// Get the properties
cmPropertyMap& GetProperties() { return this->Properties; }
const cmPropertyMap& GetProperties() const { return this->Properties; }
/**
* Check whether the given source file location could refer to this
......
......@@ -681,20 +681,40 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup()
if (const char* g = (*oi)->GetProperty("VS_RESOURCE_GENERATOR")) {
generator = g;
}
this->WriteString("<Generator>", 3);
(*this->BuildFileStream) << cmVS10EscapeXML(generator)
<< "</Generator>\n";
if (designerResource.find(srcDir) == 0) {
designerResource = designerResource.substr(srcDir.length() + 1);
} else if (designerResource.find(binDir) == 0) {
designerResource = designerResource.substr(binDir.length() + 1);
} else {
designerResource =
cmsys::SystemTools::GetFilenameName(designerResource);
if (!generator.empty()) {
this->WriteString("<Generator>", 3);
(*this->BuildFileStream) << cmVS10EscapeXML(generator)
<< "</Generator>\n";
if (designerResource.find(srcDir) == 0) {
designerResource = designerResource.substr(srcDir.length() + 1);
} else if (designerResource.find(binDir) == 0) {
designerResource = designerResource.substr(binDir.length() + 1);
} else {
designerResource =
cmsys::SystemTools::GetFilenameName(designerResource);
}
this->ConvertToWindowsSlash(designerResource);
this->WriteString("<LastGenOutput>", 3);
(*this->BuildFileStream) << designerResource
<< "</LastGenOutput>\n";
}
}
const cmPropertyMap& props = (*oi)->GetProperties();
for (cmPropertyMap::const_iterator p = props.begin(); p != props.end();
++p) {
static const std::string propNamePrefix = "VS_CSHARP_";
if (p->first.find(propNamePrefix.c_str()) == 0) {
std::string tagName = p->first.substr(propNamePrefix.length());
if (!tagName.empty()) {
std::string value = props.GetPropertyValue(p->first);
if (!value.empty()) {
this->WriteString("<", 3);
(*this->BuildFileStream) << tagName << ">";
(*this->BuildFileStream) << cmVS10EscapeXML(value);
(*this->BuildFileStream) << "</" << tagName << ">\n";
}
}
}
this->ConvertToWindowsSlash(designerResource);
this->WriteString("<LastGenOutput>", 3);
(*this->BuildFileStream) << designerResource << "</LastGenOutput>\n";
}
}
......@@ -1941,42 +1961,21 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
sourceFileTags["Link"] = link;
}
}
// check if file is a generated .Designer.cs or .xaml.cs file
// to add additional necessary tags
const std::string fileExtension =
cmsys::SystemTools::GetFilenameExtension(f);
if (fileExtension == ".Designer.cs" || fileExtension == ".xaml.cs") {
f = f.substr(0, f.length() - fileExtension.length());
if (sourceFileTags.find("Link") == sourceFileTags.end() &&
!this->InSourceBuild) {
// add link fallback
sourceFileTags["Link"] =
cmsys::SystemTools::GetFilenameName(f) + fileExtension;
}
std::vector<std::string> extensions;
extensions.push_back(".resx");
extensions.push_back(".settings");
extensions.push_back(".xaml");
extensions.push_back(".cs");
std::string dependencyExtension;
for (std::vector<std::string>::iterator i = extensions.begin();
i != extensions.end(); ++i) {
if (cmsys::SystemTools::FileExists(f + *i)) {
dependencyExtension = *i;
// There should never be more than one match. Otherwise
// one cannot tell on which match the file depends.
break;
const cmPropertyMap& props = sf.GetProperties();
for (cmPropertyMap::const_iterator p = props.begin(); p != props.end();
++p) {
static const std::string propNamePrefix = "VS_CSHARP_";
if (p->first.find(propNamePrefix.c_str()) == 0) {
std::string tagName = p->first.substr(propNamePrefix.length());
if (!tagName.empty()) {
const std::string val = props.GetPropertyValue(p->first);
if (!val.empty()) {
sourceFileTags[tagName] = val;
} else {
sourceFileTags.erase(tagName);
}
}
}
if (dependencyExtension == ".resx") {
sourceFileTags["DesignTime"] = "True";
sourceFileTags["AutoGen"] = "True";
} else if (dependencyExtension == ".settings") {
sourceFileTags["DesignTimeSharedInput"] = "True";
sourceFileTags["AutoGen"] = "True";
}
sourceFileTags["DependentUpon"] =
cmsys::SystemTools::GetFilenameName(f) + dependencyExtension;
}
// write source file specific tags
if (!sourceFileTags.empty()) {
......
......@@ -3,3 +3,4 @@ run_cmake(VsConfigurationType)
run_cmake(VsTargetsFileReferences)
run_cmake(VsCustomProps)
run_cmake(VsDebuggerWorkingDir)
run_cmake(VsCSharpCustomTags)
set(csProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.csproj")
if(NOT EXISTS "${csProjectFile}")
set(RunCMake_TEST_FAILED "Project file ${csProjectFile} does not exist.")
return()
endif()
set(tagFound FALSE)
set(tagName "MyCustomTag")
set(tagValue "MyCustomValue")
file(STRINGS "${csProjectFile}" lines)
foreach(line IN LISTS lines)
if(line MATCHES "^ *<${tagName}>${tagValue}</${tagName}>")
message(STATUS "foo.csproj has tag ${tagName} with value ${tagValue} defined")
set(tagFound TRUE)
endif()
endforeach()
if(NOT tagFound)
set(RunCMake_TEST_FAILED "Source file tag ${tagName} with value ${tagValue} not found.")
return()
endif()
enable_language(CSharp)
add_library(foo foo.cs)
set(props_file "${CMAKE_CURRENT_SOURCE_DIR}/my.props")
set(tagName "MyCustomTag")
set(tagValue "MyCustomValue")
set_source_files_properties(foo.cs
PROPERTIES
VS_CSHARP_${tagName} "${tagValue}")
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment