diff options
Diffstat (limited to 'Documentation')
-rw-r--r-- | Documentation/coding-guidelines/project-guidelines.md | 58 |
1 files changed, 28 insertions, 30 deletions
diff --git a/Documentation/coding-guidelines/project-guidelines.md b/Documentation/coding-guidelines/project-guidelines.md index a0958a3a45..18663eaa37 100644 --- a/Documentation/coding-guidelines/project-guidelines.md +++ b/Documentation/coding-guidelines/project-guidelines.md @@ -1,12 +1,12 @@ #Library Project guidelines -Library projects should use the fllowing directory layout. +Library projects should use the following directory layout. ``` src\<Library Name>\src - Contains the source code for the library. src\<Library Name>\ref - Contains any reference assembly projects for the library src\<Library Name>\pkg - Contains package projects for the library. src\<Library Name>\tests - Contains the test code for a library ``` -In the src directory for a library there should be only **one** `.csproj` file that contains any information necessary to build the library in various configurartions (see [Configurations](#project-configuration-conventions)). The src directory should also contain exactly **one** `.builds` file which contains all the valid configurations that a library should be built for (see [#.builds file](#project-builds-file)). In some cases there might be a Facade subdirectory under src with a .csproj (see [Facades projects](#facades-projects)). +In the src directory for a library there should be only **one** `.csproj` file that contains any information necessary to build the library in various configurartions (see [Configurations](#project-configuration-conventions)). The src directory should also contain exactly **one** `.builds` file which contains all the valid configurations that a library should be built for (see [#.builds file](#project-builds-file)). In the ref directory for the library there should be at most **one** `.csproj` that contains the latest API for the reference assembly for the library. If a library cannot support all supported targets using the latest API it should use `.depproj` projects for each significant historical version in order to download and redistribute the historical reference assembly in the latest package. If a library is a pure portable library with a single implementation it need not use a reference assembly at all. (see [Reference assembly projects](#reference-assembly-projects)). @@ -18,8 +18,8 @@ Below is a list of all the various options we pivot the project builds on. - **Architecture:** x86, x64, ARM, ARM64 - **Flavor:** Debug, Release - **OS:** Windows_NT, Linux, OSX, FreeBSD -- **Platform Runtimes:** NetFx (aka CLR/Desktop), CoreCLR, NetNative (aka MRT/AOT) -- **Target Frameworks:** NetFx (aka Desktop), .NETCore (aka Store/UWP), NETCoreApp (aka .NET Core), netstandard(aka dotnet/Portable) +- **Platform Runtimes:** NetFx (aka CLR/Desktop), CoreCLR, CoreRT (aka NetNative/AOT/MRT) +- **Target Frameworks:** NetFx (aka Desktop), netstandard (aka dotnet/Portable), NETCoreApp (aka .NET Core), UAP (aka UWP/Store/netcore50) - **Version:** Potentially multiple versions at the same time. ##Full Repo build pass @@ -38,7 +38,7 @@ For each unique configuration needed for a given library project a configuration - `$(Configuration) -> $(OSGroup)_$(TargetGroup)_$(ConfigurationGroup)` - `$(OSGroup) -> [Empty]/AnyOS* | Windows | Linux | OSX | FreeBSD` - `$(TargetGroup) -> [Empty]* | <PackageTargetFramework> | <PackageTargetRuntime> | <Version> | <PackageTargetFramework><PackageTargetRuntime>` - - `$(PackageTargetFramework) -> netstandard1.x/(currently dotnet5.x) | netcore50 | net46 | netcoreapp1.0` + - `$(PackageTargetFramework) -> net46x | netstandard1.x | netcoreapp1.x | uap10.x | netcore50` - `$(PackageTargetRuntime) -> aot` - For more information on various targets see also [.NET Platform Standard](https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-platform-standard.md) - `$(ConfigurationGroup) -> Debug* | Release` @@ -60,18 +60,17 @@ Project configurations with a unique implementation for each OS <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'OSX_Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Windows_Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Windows_Release|AnyCPU'" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Windows_netcore50_Debug|AnyCPU'" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Windows_netcore50_Release|AnyCPU'" /> ``` Project configurations that are unique for a few different target frameworks and runtimes ``` <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcore50aot_Debug|AnyCPU'" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcore50aot_Release|AnyCPU'" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcore50_Debug|AnyCPU'" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcore50_Release|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap101aot_Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap101aot_Release|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap101_Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap101_Release|AnyCPU'" /> ``` + ## Project .builds file To drive the Project build pass we have a `.builds` project file that will multiplex the various optional build parameters we have for all the various configurtions within a given build pass. @@ -106,12 +105,9 @@ Project configurations with a unique implementation for each OS <Project Include="System.IO.FileSystem.csproj"> <OSGroup>Windows_NT</OSGroup> </Project> - <Project Include="facade\System.IO.FileSystem.csproj"> - <TargetGroup>net46</TargetGroup> - </Project> <Project Include="System.IO.FileSystem.csproj"> <OSGroup>Windows_NT</OSGroup> - <TargetGroup>netcore50</TargetGroup> + <TargetGroup>uap101</TargetGroup> </Project> </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" /> @@ -125,24 +121,21 @@ Project configurations that are unique for a few different target frameworks and <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> <ItemGroup> <Project Include="System.Linq.Expressions.csproj" /> - <Project Include="Facade\System.Linq.Expressions.csproj"> - <TargetGroup>net46</TargetGroup> - </Project> <Project Include="System.Linq.Expressions.csproj"> <OSGroup>Windows_NT</OSGroup> - <TargetGroup>netcore50</TargetGroup> + <TargetGroup>uap101</TargetGroup> </Project> <Project Include="System.Linq.Expressions.csproj"> <OSGroup>Windows_NT</OSGroup> - <TargetGroup>netcore50aot</TargetGroup> + <TargetGroup>uap101aot</TargetGroup> </Project> </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" /> </Project> ``` -##Facades projects -Facade projects are unique in that they don't have any code and instead are generated by finding a contract reference assembly with the matching identity and generating type forwards for all the types to where they live in the implementation assemblies (aka facade seeds). There are also partial facades which contain some type forwards as well as some code definitions. Ideally all the various build configurations would be contained in the one csproj file per library but given the unique nature of facades and the fact they are usually a complete fork of everything in the project file it is recommended to create a matching csproj under a Facade directory (<library>\src\Facade\<library>.csproj) and reference that from your .builds file, as in the System.Linq.Expressions example. +##Facades +Facade are unique in that they don't have any code and instead are generated by finding a contract reference assembly with the matching identity and generating type forwards for all the types to where they live in the implementation assemblies (aka facade seeds). There are also partial facades which contain some type forwards as well as some code definitions. All the various build configurations should be contained in the one csproj file per library. TODO: Fill in more information about the required properties for creatng a facade project. @@ -153,22 +146,27 @@ While our goal is to have the exact code for every configuration there is always 2. Using entirely different code files for cases were the entire class (or perhaps static class) needs to be unique in a given configuration. 3. Using `#ifdef`'s directly in a shared code file. -In general we perfer different code files over `#ifdef`'s because it forces us to better factor the code which leads to easier maintenance over time. +In general we prefer different code files over `#ifdef`'s because it forces us to better factor the code which leads to easier maintenance over time. ## Code file naming conventions Each source file should use the following guidelines -- The source code file should contain only one class. The only exception is small supporting structs, enums, nested classes, or delegates that only apply to the class can also be contained in the source file. -- The source code file should be named `<class>.cs` and should be placed in a directory structure that matches its namespace relative to its project directory. Ex. `System\IO\Stream.cs` +- The source code file should contain only one class. The only exception is small supporting structs, enums, nested classes, or delegates that only apply to the class can also be contained in the source file. +- The source code file should be named `<class>.cs` and should be placed in a directory structure that matches its namespace relative to its project directory. Ex. `System\IO\Stream.cs` - Larger nested classes should be factored out into their own source files using a partial class and the file name should be `<class>.<nested class>.cs`. -- Classes that are forked based on configuration should have file names `<class>.<configuration>.cs`. +- Classes that are forked based on configuration should have file names `<class>.<configuration>.cs`. - Where `<configuration>` is one of `$(OSGroup)`, `$(TargetGroup)`, `$(ConfigurationGroup)`, or `$(Platform)`, matching exactly by case to ensure consistency. - Classes that are forked based on a feature set should have file names `<class>.<feature>.cs`. - - Where `<feature>`is the name of something that causes a fork in code that isn't a configuration such as WinRT support. Ex. `FileSystemInfo.WinRT.cs`. - + - Where `<feature>` is the name of something that causes a fork in code that isn't a single configuration. Examples: + - `.CoreCLR.cs` - implementation specific to CoreCLR runtime + - `.CoreRT.cs` - implementation specific to CoreRT runtime + - `.Win32.cs` - implementation based on [Win32](https://en.wikipedia.org/wiki/Windows_API) + - `.WinRT.cs` - implementation based on [WinRT](https://en.wikipedia.org/wiki/Windows_Runtime) + - `.Uap.cs` - implementation specific to UAP, also known as [UWP](https://en.wikipedia.org/wiki/Universal_Windows_Platform) + ## Define naming convention As mentioned in [Conventions for forked code](conventions-for-forked-code) `#ifdef`ing the code is the last resort as it makes code harder to maintain overtime. If we do need to use `#ifdef`'s we should use the following conventions: -- Defines based on conventions should be one of `$(OSGroup)`, `$(TargetGroup)`, `$(ConfigurationGroup)`, or `$(Platform)`, matching exactly by case to ensure consistency. - - Examples: `<DefineConstants>$(DefineConstants),netcore50</DefineContants>` +- Defines based on conventions should be one of `$(OSGroup)`, `$(TargetGroup)`, `$(ConfigurationGroup)`, or `$(Platform)`, matching exactly by case to ensure consistency. + - Examples: `<DefineConstants>$(DefineConstants),net46</DefineContants>` - Defines based on convention should match the pattern `FEATURE_<feature name>`. These can unique to a given library project or potentially shared (via name) across multiple projects. # Reference assembly projects |