diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml new file mode 100644 index 000000000..dc9575d50 --- /dev/null +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -0,0 +1,32 @@ +## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details. + +pipelines: + 286: + retail: + binary: + credscan: + lastModifiedDate: 2024-03-15 + binskim: + lastModifiedDate: 2024-03-15 + spotbugs: + lastModifiedDate: 2024-03-15 + source: + credscan: + lastModifiedDate: 2024-03-15 + eslint: + lastModifiedDate: 2024-03-15 + psscriptanalyzer: + lastModifiedDate: 2024-03-15 + armory: + lastModifiedDate: 2024-03-15 + 1226: + retail: + source: + credscan: + lastModifiedDate: 2024-03-25 + eslint: + lastModifiedDate: 2024-03-25 + psscriptanalyzer: + lastModifiedDate: 2024-03-25 + armory: + lastModifiedDate: 2024-03-25 diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 000000000..b8ea93944 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "microsoft.dotnet.darc": { + "version": "1.1.0-beta.24256.1", + "commands": [ + "darc" + ] + } + } +} diff --git a/.config/guardian/.gdnbaselines b/.config/guardian/.gdnbaselines new file mode 100644 index 000000000..66ea5cc98 --- /dev/null +++ b/.config/guardian/.gdnbaselines @@ -0,0 +1,225 @@ +{ + "properties": { + "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines" + }, + "version": "1.0.0", + "baselines": { + "default": { + "name": "default", + "createdDate": "2024-03-15 21:07:20Z", + "lastUpdatedDate": "2024-03-15 21:07:20Z" + } + }, + "results": { + "9fcdc81730d500a234ad1fcd1a18108502b8c50c48567b91176c8e26f6bc5d34": { + "signature": "9fcdc81730d500a234ad1fcd1a18108502b8c50c48567b91176c8e26f6bc5d34", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/nuget.config", + "line": 9, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-03-15 21:07:20Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "8814a931c03f7ee625e291e19f2f5671357142600639b0143a3c46631b864e93": { + "signature": "8814a931c03f7ee625e291e19f2f5671357142600639b0143a3c46631b864e93", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/dlabnugetcert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "29ef09fbcdcda70063644424ad6a64c7aaae97e31b89c31c89b631eab56252e9": { + "signature": "29ef09fbcdcda70063644424ad6a64c7aaae97e31b89c31c89b631eab56252e9", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/dynamicsha1.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "5801a584cc6df8c732f18f7abc1b1ccf64ab2a6ddbb05f9bbdde8beb5a67fe3a": { + "signature": "5801a584cc6df8c732f18f7abc1b1ccf64ab2a6ddbb05f9bbdde8beb5a67fe3a", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/dynamicsha2.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "cab715138e57370e027c311bdcfcd828aef2e813338eb9b6db44c222132dcc43": { + "signature": "cab715138e57370e027c311bdcfcd828aef2e813338eb9b6db44c222132dcc43", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/testdlab.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "69fdade2005d4c82978110367d00ff0b563d127022c89cc13d9130ff08811249": { + "signature": "69fdade2005d4c82978110367d00ff0b563d127022c89cc13d9130ff08811249", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/testdlabsha2.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "efe01799a8cef937301b58208e2b347b9f5c534d89815d9843d75be7b2e2fb43": { + "signature": "efe01799a8cef937301b58208e2b347b9f5c534d89815d9843d75be7b2e2fb43", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/vsmsappx.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "f5eed94d081284181a2cd78b47b336ece0fcf2c0eafd462ffe6a88ff913d8184": { + "signature": "f5eed94d081284181a2cd78b47b336ece0fcf2c0eafd462ffe6a88ff913d8184", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/WinBlue.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "3aa029a27ef41acb56bd41ac97412078144ad5e722c21c325333f3af37b51d8a": { + "signature": "3aa029a27ef41acb56bd41ac97412078144ad5e722c21c325333f3af37b51d8a", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/WP223.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "624055be3115da111704bd2823e64fb78862b3650767e571aef8a9b7a127c6d8": { + "signature": "624055be3115da111704bd2823e64fb78862b3650767e571aef8a9b7a127c6d8", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/MicroBuild.Plugins.Signing.1.1.902/build/tools/MobileTools/7Sign/tcb.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "1d4ee8b341d5f1ef56c8e517759511e55556866f0a6fbabf23440798da1f05d5": { + "signature": "1d4ee8b341d5f1ef56c8e517759511e55556866f0a6fbabf23440798da1f05d5", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/nuget.config", + "line": 9, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-03-15 21:10:23Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "4fdcce44747d67552fa68a352060f50d85431c0c7ea4215b395bdb360db739a7": { + "signature": "4fdcce44747d67552fa68a352060f50d85431c0c7ea4215b395bdb360db739a7", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/nuget.config", + "line": 9, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "62685d821fc38b8db7ddb0a07decba2a1be29c26ce13b5cb75fcc123ad048783": { + "signature": "62685d821fc38b8db7ddb0a07decba2a1be29c26ce13b5cb75fcc123ad048783", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/nuget.config", + "line": 9, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-03-15 21:10:55Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "0ecc3cb201b58b5026eef9239d3a888de6188f974948d8c2d76ea5f22dd12fd2": { + "signature": "0ecc3cb201b58b5026eef9239d3a888de6188f974948d8c2d76ea5f22dd12fd2", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/nuget.config", + "line": 9, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-03-15 21:10:23Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + }, + "e4e0934b769d92960066e0db46b39bcb24f69fb478779b630d2d8581f384a95c": { + "signature": "e4e0934b769d92960066e0db46b39bcb24f69fb478779b630d2d8581f384a95c", + "alternativeSignatures": [], + "target": "MicroBuild/Plugins/nuget.config", + "line": 9, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-03-15 21:10:36Z", + "expirationDate": "2024-09-01 21:43:51Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-15 21:43:51Z" + } + } +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..d0c5a7fc5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,149 @@ +# editorconfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 + +[project.json] +indent_size = 2 + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_within_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# only use var when it's obvious what the variable type is +csharp_style_var_for_built_in_types = false:none +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = false:none + +# use language keywords instead of BCL types +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style + +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal + +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code style defaults +dotnet_sort_system_directives_first = true +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# C++ Files +[*.{cpp,h,in}] +curly_bracket_next_line = true +indent_brace_style = Allman + +# Xml project files +[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] +indent_size = 2 + +# Xml build files +[*.builds] +indent_size = 2 + +# Xml files +[*.{xml,stylecop,resx,ruleset}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +# Shell scripts +[*.sh] +end_of_line = lf +[*.{cmd, bat}] +end_of_line = crlf diff --git a/.forgejo/workflows/generate-tarball.yml b/.forgejo/workflows/generate-tarball.yml deleted file mode 100644 index b85e30c36..000000000 --- a/.forgejo/workflows/generate-tarball.yml +++ /dev/null @@ -1,70 +0,0 @@ -on: - workflow_dispatch: - inputs: - ref_name: - description: 'Tag or commit' - required: true - type: string - - push: - tags: - - '*' - -jobs: - build-tarball: - name: Build tarball - runs-on: x86_64 - container: - image: alpine:3.20 - steps: - - name: Environment setup - run: apk add nodejs git xz gzip dotnet6-sdk sed bash - - name: Repo pull - uses: actions/checkout@v4 - with: - fetch-depth: 500 - ref: ${{ inputs.ref_name }} - - name: Package build - run: | - if test $GITHUB_REF_NAME == "ci" ; then - CI_REF_NAME=${{ inputs.ref_name }} - else - CI_REF_NAME=$GITHUB_REF_NAME - fi - sed 's||' -i src/SourceBuild/Arcade/tools/SourceBuildArcadeTarball.targets - export _cli_root=/usr/lib/dotnet - echo "Building dotnet-$CI_REF_NAME tarball" - _InitializeDotNetCli="$_cli_root" DOTNET_INSTALL_DIR="$_cli_root" DotNetBuildFromSource=true ./build.sh /p:ArcadeBuildTarball=true /p:EnableSourceLink=false /p:CheckEolTargetFramework=false /p:TarballDir=$(pwd)/dotnet-$CI_REF_NAME /p:TarballFilePath=$(pwd)/dotnet-$CI_REF_NAME.tar - echo "Compressing dotnet-$CI_REF_NAME.tar.xz" - xz -T0 -9 -vv -e -c > ./dotnet-$CI_REF_NAME.tar.xz < ./dotnet-$CI_REF_NAME.tar - echo "Generating sha512sum and sha256sum" - sha512sum dotnet-$CI_REF_NAME.tar.xz > dotnet-$CI_REF_NAME.tar.xz.sha512sum - sha256sum dotnet-$CI_REF_NAME.tar.xz > dotnet-$CI_REF_NAME.tar.xz.sha256sum - - name: Package upload - uses: forgejo/upload-artifact@v3 - with: - name: tarball - path: dotnet*.tar.* - retention-days: 1 day - upload-tarball: - name: Upload to generic repo - runs-on: x86_64 - needs: [build-tarball] - container: - image: alpine:latest - steps: - - name: Environment setup - run: apk add nodejs curl findutils - - name: Package download - uses: forgejo/download-artifact@v3 - - name: Package deployment - run: | - if test $GITHUB_REF_NAME == "ci" ; then - CI_REF_NAME=${{ inputs.ref_name }} - else - CI_REF_NAME=$GITHUB_REF_NAME - fi - echo "Uploading to ${{ github.server.url }}/api/packages/mirrors/generic/dotnet/$CI_REF_NAME/dotnet-$CI_REF_NAME.tar.xz" - curl --user ${{ vars.CODE_FORGEJO_USER }}:${{ secrets.CODE_FORGEJO_TOKEN }} --upload-file ./tarball/dotnet-*.tar.xz ${{ github.server_url }}/api/packages/mirrors/generic/dotnet/$CI_REF_NAME/dotnet-$CI_REF_NAME.tar.xz - curl --user ${{ vars.CODE_FORGEJO_USER }}:${{ secrets.CODE_FORGEJO_TOKEN }} --upload-file ./tarball/dotnet-*.tar.xz.sha512sum ${{ github.server_url }}/api/packages/mirrors/generic/dotnet/$CI_REF_NAME/dotnet-$CI_REF_NAME.tar.xz.sha512sum - curl --user ${{ vars.CODE_FORGEJO_USER }}:${{ secrets.CODE_FORGEJO_TOKEN }} --upload-file ./tarball/dotnet-*.tar.xz.sha256sum ${{ github.server_url }}/api/packages/mirrors/generic/dotnet/$CI_REF_NAME/dotnet-$CI_REF_NAME.tar.xz.sha256sum diff --git a/.forgejo/workflows/mirror-repository.yml b/.forgejo/workflows/mirror-repository.yml deleted file mode 100644 index 8263173b7..000000000 --- a/.forgejo/workflows/mirror-repository.yml +++ /dev/null @@ -1,50 +0,0 @@ -on: - workflow_dispatch: - - schedule: - - cron: '@hourly' - -jobs: - mirror: - name: Pull from upstream - runs-on: x86_64 - container: - image: alpine:latest - env: - upstream: https://github.com/dotnet/installer - tags: 'v6.0.1*' - steps: - - name: Environment setup - run: apk add grep git sed coreutils bash nodejs - - name: Fetch destination - uses: actions/checkout@v4 - with: - fetch_depth: 1 - ref: ci - token: ${{ secrets.CODE_FORGEJO_TOKEN }} - - name: Missing tag detecting - run: | - git ls-remote $upstream "refs/tags/$tags" | grep -v '{' | sed 's|.*/||' | sort > upstream_tags - git ls-remote ${{ github.server_url}}/${{ github.repository }} "refs/tags/$tags" | grep -v '{' | sed 's|.*/||' | sort > destination_tags - comm -23 upstream_tags destination_tags > missing_tags - echo "Missing tags:" - cat missing_tags - - name: Missing tag fetch - run: | - git remote add upstream $upstream - while read tag; do - git fetch upstream tag $tag --no-tags - done < missing_tags - - name: Packaging workflow injection - run: | - while read tag; do - git checkout $tag - git tag -d $tag - git checkout ci -- ./.forgejo - git config user.name "forgejo-actions[bot]" - git config user.email "dev@ayakael.net" - git commit -m 'Inject custom workflow' - git tag -a $tag -m $tag - done < missing_tags - - name: Push to destination - run: git push --force origin refs/tags/*:refs/tags/* --tags diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..6bd3ace83 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,55 @@ +*.doc binary +*.DOC binary +*.docx binary +*.DOCX binary +*.dot binary +*.DOT binary +*.pdf binary +*.PDF binary +*.rtf binary +*.RTF binary + +*.jpg binary +*.png binary +*.gif binary + +*.sh eol=lf + +*.cs text=auto diff=csharp +*.vb text=auto +*.resx text=auto +*.c text=auto +*.cpp text=auto +*.cxx text=auto +*.h text=auto +*.hxx text=auto +*.py text=auto +*.rb text=auto +*.java text=auto +*.html text=auto +*.htm text=auto +*.css text=auto +*.scss text=auto +*.sass text=auto +*.less text=auto +*.js text=auto +*.lisp text=auto +*.clj text=auto +*.sql text=auto +*.php text=auto +*.lua text=auto +*.m text=auto +*.asm text=auto +*.erl text=auto +*.fs text=auto +*.fsx text=auto +*.hs text=auto + +*.props text=auto +*.targets text=auto +*.csproj text=auto +*.vbproj text=auto +*.fsproj text=auto +*.dbproj text=auto +*.xlf text=auto +*.sln text=auto eol=crlf diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 000000000..60eb59ab4 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,17 @@ +name: Backport PR to branch +on: + issue_comment: + types: [created] + schedule: + # once a day at 13:00 UTC to cleanup old runs + - cron: '0 13 * * *' + +permissions: + contents: write + issues: write + pull-requests: write + actions: write + +jobs: + backport: + uses: dotnet/arcade/.github/workflows/backport-base.yml@main diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e334dae58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,312 @@ +### Repo-specific things ### + +# Test results +*-testResults.xml + +# Multicore JIT Optimization profiles +**/optimizationdata/dotnet + +# Test Asset Manager files +.tam/ + +# NuGet keeps dropping +Library/ + +# local nuget cache +.nuget/ + +# NuGet v3 restore drops these even though we don't use MSBuild :( +*.nuget.targets +*.nuget.props + +# Debian and python stuff +*.dsc +*.tar.gz +*.build +*.changes +*.deb +*.pyc +*.pyo + +# OSX Packaging temp files +*.pkg + +# OS X Device Services Store +.DS_Store + +# CMake generated files +cmake/ + +# stage0 install directory +.dotnet_stage0 + +# build tools directory +build_tools + +# `dotnet new` template zip files are generated by a pre-build step. +/src/dotnet/commands/dotnet-new/*.zip + +### VisualStudio.gitignore from https://raw.githubusercontent.com/github/gitignore/master/VisualStudio.gitignore ### + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ + +# Visual Studio Code cache/options directory +.vscode/ + +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Build artifacts +artifacts/ +artifacts-*/ +bin/ +.dotnet/ +.dotnet-*/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.binlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# BinLog artifacts +msbuild*ProjectImports.zip + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user +.idea/ +*.iml +*.DotSettings + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directory +AppPackages/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ + +# Exceptions +# Build Scripts +!scripts/build/ +test/PackagedCommands/Consumers/*/project.json + +# Vim swp files +*.swp +*.*~ + +# VS generated files +launchSettings.json + +# user files used in tests +!TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/TestLibrary.xproj.user +!TestAssets/TestProjects/TestAppWithLibrary/TestApp/TestApp.xproj.user +!TestAssets/TestProjects/PJTestAppSimple/PJTestAppSimple.xproj.user + +# Local nuget cache when the build is run as CI +.packages \ No newline at end of file diff --git a/.vsts-ci.yml b/.vsts-ci.yml new file mode 100644 index 000000000..302aad5ed --- /dev/null +++ b/.vsts-ci.yml @@ -0,0 +1,387 @@ +# Pipeline: https://dnceng.visualstudio.com/internal/_build?definitionId=286 + +trigger: + batch: true + branches: + include: + - main + - master + - release/* + - internal/release/* + +variables: +- name: _PublishUsingPipelines + value: false +- ${{ if or(startswith(variables['Build.SourceBranch'], 'refs/heads/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), eq(variables['Build.Reason'], 'Manual')) }}: + - name: PostBuildSign + value: false +- ${{ else }}: + - name: PostBuildSign + value: true +- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - name: Codeql.Enabled + value: true + - group: DotNet-DotNetCli-Storage + - group: DotNet-Installer-SDLValidation-Params + - name: _PublishUsingPipelines + value: true +- name: _InternalRuntimeDownloadArgs + value: '' +- ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - group: DotNetBuilds storage account read tokens + - name: _InternalRuntimeDownloadArgs + value: /p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal + /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) + /p:dotnetbuilds-internal-container-read-token-base64=$(dotnetbuilds-internal-container-read-token-base64) +- template: /eng/common/templates-official/variables/pool-providers.yml +# Set the MicroBuild plugin installation directory to the agent temp directory to avoid SDL tool scanning. +- name: MicroBuildOutputFolderOverride + value: $(Agent.TempDirectory) + +resources: + repositories: + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + ${{ else }}: + template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines + parameters: + containers: + alpine319WithNode: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.19-WithNode + cblMariner20Fpm: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-fpm + centosStream9: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9 + debian11Amd64: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:debian-11-amd64 + fedora39: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-39 + ubuntu2204: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04 + mariner20CrossArm: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-cross-arm-alpine + ubuntu2204DebPkg: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-debpkg + sdl: + sourceAnalysisPool: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + stages: + - stage: Build + jobs: + # Build Retry Configuration + - job: Publish_Build_Configuration + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + image: 1es-windows-2022-open + os: windows + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + steps: + - task: 1ES.PublishPipelineArtifact@1 + displayName: Publish Build Config + inputs: + targetPath: $(Build.SourcesDirectory)\eng\buildConfiguration + artifactName: buildConfiguration + + # PR-only jobs + - ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}: + # Windows + - template: eng/build.yml@self + parameters: + agentOs: Windows_NT + jobName: Build_Debug_x64 + buildConfiguration: Debug + buildArchitecture: x64 + additionalBuildParameters: '/p:PublishInternalAsset=true' + runTests: true + + # Linux + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Ubuntu_22_04_Debug_x64 + container: ubuntu2204 + buildConfiguration: Debug + buildArchitecture: x64 + linuxPortable: true + runTests: true + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Fedora_39_Debug_x64 + container: fedora39 + buildConfiguration: Debug + buildArchitecture: x64 + linuxPortable: true + runTests: true + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_CentOS_9_Stream_Debug_x64 + container: centosStream9 + buildConfiguration: Debug + buildArchitecture: x64 + linuxPortable: false + runTests: true + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Debian_11_Debug_x64 + container: debian11Amd64 + buildConfiguration: Debug + buildArchitecture: x64 + additionalBuildParameters: '/p:BuildSdkDeb=true' + linuxPortable: false + runTests: true + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Arm64_Debug + buildConfiguration: Debug + buildArchitecture: arm64 + runtimeIdentifier: 'linux-arm64' + linuxPortable: true + # Never run tests on arm64 + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Debug_x64 + container: alpine319WithNode + buildConfiguration: Debug + buildArchitecture: x64 + runtimeIdentifier: 'linux-musl-x64' + # Pass in HostOSName when running on alpine + additionalBuildParameters: '/p:HostOSName="linux-musl"' + linuxPortable: false + runTests: true + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_LinuxPortable_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + linuxPortable: true + runTests: true + + # MacOS + - template: eng/build.yml@self + parameters: + agentOs: Darwin + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + runTests: true + + # Official/PGO instrumentation Builds + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + # Windows + - template: eng/build.yml@self + parameters: + agentOs: Windows_NT + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + additionalBuildParameters: '/p:PublishInternalAsset=true' + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Windows_NT + jobName: Build_Release_x86 + buildConfiguration: Release + buildArchitecture: x86 + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Windows_NT + jobName: Build_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + runTests: false + + # Linux + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Arm_Release + buildConfiguration: Release + buildArchitecture: arm + runtimeIdentifier: 'linux-arm' + linuxPortable: true + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Arm64_Release + buildConfiguration: Release + buildArchitecture: arm64 + runtimeIdentifier: 'linux-arm64' + linuxPortable: true + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Release_arm + container: mariner20CrossArm + buildConfiguration: Release + buildArchitecture: arm + runtimeIdentifier: 'linux-musl-arm' + additionalBuildParameters: '/p:OSName="linux-musl"' + linuxPortable: false + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + runtimeIdentifier: 'linux-musl-arm64' + additionalBuildParameters: '/p:OSName="linux-musl"' + linuxPortable: false + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Release_x64 + container: alpine319WithNode + buildConfiguration: Release + buildArchitecture: x64 + runtimeIdentifier: 'linux-musl-x64' + # Pass in HostOSName when running on alpine + additionalBuildParameters: '/p:HostOSName="linux-musl"' + linuxPortable: false + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Linux_Portable_Deb_Release_x64 + container: ubuntu2204DebPkg + buildConfiguration: Release + buildArchitecture: x64 + # Do not publish zips and tarballs. The linux-x64 binaries are + # already published by Build_LinuxPortable_Release_x64 + additionalBuildParameters: '/p:PublishBinariesAndBadge=false /p:BuildSdkDeb=true' + linuxPortable: true + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Linux_Portable_Rpm_Release_x64 + container: cblMariner20Fpm + buildConfiguration: Release + buildArchitecture: x64 + # Do not publish zips and tarballs. The linux-x64 binaries are + # already published by Build_LinuxPortable_Release_x64 + additionalBuildParameters: '/p:PublishBinariesAndBadge=false /p:IsRPMBasedDistro=true' + linuxPortable: true + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_Linux_Portable_Rpm_Release_Arm64 + container: cblMariner20Fpm + buildConfiguration: Release + buildArchitecture: arm64 + runtimeIdentifier: 'linux-arm64' + # Do not publish zips and tarballs. The linux-x64 binaries are + # already published by Build_LinuxPortable_Release_x64 + additionalBuildParameters: '/p:PublishBinariesAndBadge=false /p:CLIBUILD_SKIP_TESTS=true /p:IsRPMBasedDistro=true' + linuxPortable: true + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + jobName: Build_LinuxPortable_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + linuxPortable: true + runTests: false + + # MacOS + - template: eng/build.yml@self + parameters: + agentOs: Darwin + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Darwin + jobName: Build_Release_arm64 + runtimeIdentifier: 'osx-arm64' + buildConfiguration: Release + buildArchitecture: arm64 + runTests: false + + # Windows PGO Instrumentation + - template: eng/build.yml@self + parameters: + agentOs: Windows_NT + pgoInstrument: true + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + additionalBuildParameters: '/p:PublishInternalAsset=true' + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Windows_NT + pgoInstrument: true + jobName: Build_Release_x86 + buildConfiguration: Release + buildArchitecture: x86 + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Windows_NT + pgoInstrument: true + jobName: Build_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + runTests: false + + # Linux PGO Instrumentation + - template: eng/build.yml@self + parameters: + agentOs: Linux + pgoInstrument: true + jobName: Build_LinuxPortable_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + linuxPortable: true + runTests: false + - template: eng/build.yml@self + parameters: + agentOs: Linux + pgoInstrument: true + jobName: Build_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + linuxPortable: true + runTests: false + + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - stage: Publish + dependsOn: + - Build + jobs: + - template: /eng/common/templates-official/job/publish-build-assets.yml@self + parameters: + publishUsingPipelines: true + publishAssetsImmediately: true + pool: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows diff --git a/.vsts-pr.yml b/.vsts-pr.yml new file mode 100644 index 000000000..fb1e28d0f --- /dev/null +++ b/.vsts-pr.yml @@ -0,0 +1,361 @@ +# Pipeline: https://dev.azure.com/dnceng-public/public/_build?definitionId=20 + +trigger: + batch: true + branches: + include: + - main + - master + - release/* + - internal/release/* + +variables: +- name: _PublishUsingPipelines + value: false +- ${{ if or(startswith(variables['Build.SourceBranch'], 'refs/heads/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), eq(variables['Build.Reason'], 'Manual')) }}: + - name: PostBuildSign + value: false +- ${{ else }}: + - name: PostBuildSign + value: true +- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - name: Codeql.Enabled + value: true + - group: DotNet-DotNetCli-Storage + - group: DotNet-Installer-SDLValidation-Params + - name: _PublishUsingPipelines + value: true + +- name: _InternalRuntimeDownloadArgs + value: '' + +- ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - group: DotNetBuilds storage account read tokens + - name: _InternalRuntimeDownloadArgs + value: /p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal + /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) + /p:dotnetbuilds-internal-container-read-token-base64=$(dotnetbuilds-internal-container-read-token-base64) + +- template: /eng/common/templates/variables/pool-providers.yml + +stages: +- stage: Build + jobs: + # This job is for build retry configuration. + - job: Publish_Build_Configuration + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals windows.vs2022preview.amd64.open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022preview.amd64 + steps: + - publish: $(Build.SourcesDirectory)\eng\buildConfiguration + artifact: buildConfiguration + displayName: Publish Build Config + + ## PR-only jobs + + - ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}: + + ## Windows + + - template: eng/build-pr.yml + parameters: + agentOs: Windows_NT + jobName: Build_Debug_x64 + buildConfiguration: Debug + buildArchitecture: x64 + additionalBuildParameters: '/p:PublishInternalAsset=true' + runTests: true + + ## Linux + + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Ubuntu_22_04_Debug_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04' + buildConfiguration: Debug + buildArchitecture: x64 + linuxPortable: true + runTests: true + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Fedora_39_Debug_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-39' + buildConfiguration: Debug + buildArchitecture: x64 + linuxPortable: true + runTests: true + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_CentOS_8_Stream_Debug_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8' + buildConfiguration: Debug + buildArchitecture: x64 + linuxPortable: false + runTests: true + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Debian_11_Debug_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:debian-11-amd64' + buildConfiguration: Debug + buildArchitecture: x64 + additionalBuildParameters: '/p:BuildSdkDeb=true' + linuxPortable: false + runTests: true + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Arm64_Debug + buildConfiguration: Debug + buildArchitecture: arm64 + runtimeIdentifier: 'linux-arm64' + linuxPortable: true + # Never run tests on arm64 + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Debug_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.19-WithNode' + buildConfiguration: Debug + buildArchitecture: x64 + runtimeIdentifier: 'linux-musl-x64' + # Pass in HostOSName when running on alpine + additionalBuildParameters: '/p:HostOSName="linux-musl"' + linuxPortable: false + runTests: true + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_LinuxPortable_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + linuxPortable: true + runTests: true + + # MacOS + + - template: eng/build-pr.yml + parameters: + agentOs: Darwin + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + runTests: true + + ## Official/PGO instrumentation Builds + + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + + ## Windows + + - template: eng/build-pr.yml + parameters: + agentOs: Windows_NT + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + additionalBuildParameters: '/p:PublishInternalAsset=true' + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Windows_NT + jobName: Build_Release_x86 + buildConfiguration: Release + buildArchitecture: x86 + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Windows_NT + jobName: Build_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + runTests: false + + ## Linux + + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Arm_Release + buildConfiguration: Release + buildArchitecture: arm + runtimeIdentifier: 'linux-arm' + linuxPortable: true + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Arm64_Release + buildConfiguration: Release + buildArchitecture: arm64 + runtimeIdentifier: 'linux-arm64' + linuxPortable: true + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Release_arm + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-cross-arm-alpine' + buildConfiguration: Release + buildArchitecture: arm + runtimeIdentifier: 'linux-musl-arm' + additionalBuildParameters: '/p:OSName="linux-musl"' + linuxPortable: false + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + runtimeIdentifier: 'linux-musl-arm64' + additionalBuildParameters: '/p:OSName="linux-musl"' + linuxPortable: false + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Linux_musl_Release_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.19-WithNode' + buildConfiguration: Release + buildArchitecture: x64 + runtimeIdentifier: 'linux-musl-x64' + # Pass in HostOSName when running on alpine + additionalBuildParameters: '/p:HostOSName="linux-musl"' + linuxPortable: false + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Linux_Portable_Deb_Release_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-debpkg' + buildConfiguration: Release + buildArchitecture: x64 + # Do not publish zips and tarballs. The linux-x64 binaries are + # already published by Build_LinuxPortable_Release_x64 + additionalBuildParameters: '/p:PublishBinariesAndBadge=false /p:BuildSdkDeb=true' + linuxPortable: true + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Linux_Portable_Rpm_Release_x64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-fpm' + buildConfiguration: Release + buildArchitecture: x64 + # Do not publish zips and tarballs. The linux-x64 binaries are + # already published by Build_LinuxPortable_Release_x64 + additionalBuildParameters: '/p:PublishBinariesAndBadge=false /p:IsRPMBasedDistro=true' + linuxPortable: true + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_Linux_Portable_Rpm_Release_Arm64 + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-fpm' + buildConfiguration: Release + buildArchitecture: arm64 + runtimeIdentifier: 'linux-arm64' + # Do not publish zips and tarballs. The linux-x64 binaries are + # already published by Build_LinuxPortable_Release_x64 + additionalBuildParameters: '/p:PublishBinariesAndBadge=false /p:CLIBUILD_SKIP_TESTS=true /p:IsRPMBasedDistro=true' + linuxPortable: true + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Linux + jobName: Build_LinuxPortable_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + linuxPortable: true + runTests: false + + # MacOS + + - template: eng/build-pr.yml + parameters: + agentOs: Darwin + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Darwin + jobName: Build_Release_arm64 + runtimeIdentifier: 'osx-arm64' + buildConfiguration: Release + buildArchitecture: arm64 + runTests: false + + ## Windows PGO Instrumentation builds + + - template: eng/build-pr.yml + parameters: + agentOs: Windows_NT + pgoInstrument: true + jobName: Build_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + additionalBuildParameters: '/p:PublishInternalAsset=true' + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Windows_NT + pgoInstrument: true + jobName: Build_Release_x86 + buildConfiguration: Release + buildArchitecture: x86 + runTests: false + - template: eng/build-pr.yml + parameters: + agentOs: Windows_NT + pgoInstrument: true + jobName: Build_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + runTests: false + + ## Linux PGO Instrumentation builds + + - template: eng/build-pr.yml + parameters: + agentOs: Linux + pgoInstrument: true + jobName: Build_LinuxPortable_Release_x64 + buildConfiguration: Release + buildArchitecture: x64 + linuxPortable: true + runTests: false + + - template: eng/build-pr.yml + parameters: + agentOs: Linux + pgoInstrument: true + jobName: Build_Release_arm64 + buildConfiguration: Release + buildArchitecture: arm64 + linuxPortable: true + runTests: false + +- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - stage: Publish + dependsOn: + - Build + jobs: + - template: /eng/common/templates/job/publish-build-assets.yml + parameters: + publishUsingPipelines: true + publishAssetsImmediately: true + pool: + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md new file mode 100644 index 000000000..775f221c9 --- /dev/null +++ b/CODE-OF-CONDUCT.md @@ -0,0 +1,6 @@ +# Code of Conduct + +This project has adopted the code of conduct defined by the Contributor Covenant +to clarify expected behavior in our community. + +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..6b52edc80 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,4 @@ +# Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths. +# See https://help.github.com/articles/about-code-owners/ + +/src/snaps/ @rbhanda \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..3e0f963fa --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +Contributing +============ + +See [Contributing](https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/contributing.md) for information about coding styles, source structure, making pull requests, and more. + +Developers +========== + +If you are looking to contribute to the verbs that ship with the SDK, please go to the [CLI Repo](https://github.com/dotnet/cli). \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..c684ed612 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,56 @@ + + + + + $([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant()) + $(BuildArchitecture) + $(BuildArchitecture) + $(BuildArchitecture) + $(BuildArchitecture) + $(BuildArchitecture) + x64 + + + $(Architecture) + + + + + + $(CopyrightNetFoundation) + MIT + + + + true + -pgo + + + + True + embedded + true + + true + net9.0 + + + NU5125;NU5105;NU1701;MSB3243;MSB3247 + + + + false + false + false + + https://github.com/dotnet/core-sdk + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..055028b64 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..74dd8fbae --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Microsoft.DotNet.Cli.sln b/Microsoft.DotNet.Cli.sln new file mode 100644 index 000000000..12f6e66eb --- /dev/null +++ b/Microsoft.DotNet.Cli.sln @@ -0,0 +1,67 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34112.27 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED2FE3E2-F7E7-4389-8231-B65123F2076F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "redist", "src\redist\redist.csproj", "{098D9321-1201-4974-A75E-F58EBCD98ACF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EndToEnd.Tests", "test\EndToEnd\EndToEnd.Tests.csproj", "{1BFF54F9-4E35-49DB-893C-AF2047722FC6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "core-sdk-tasks", "src\core-sdk-tasks\core-sdk-tasks.csproj", "{53AF2D01-B69F-4CD0-86A7-8FD95967D23C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Tools.Tests.Utilities", "test\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj", "{78E15EC1-7732-41E3-8591-934E9F583254}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SdkTests", "test\SdkTests\SdkTests.csproj", "{CB1EE94E-CB83-4071-9DD0-9929AE2B7282}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "core-sdk-tasks.Tests", "test\core-sdk-tasks.Tests\core-sdk-tasks.Tests.csproj", "{658EF9BE-452C-4D31-AA24-B9E2235799A8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {098D9321-1201-4974-A75E-F58EBCD98ACF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {098D9321-1201-4974-A75E-F58EBCD98ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {098D9321-1201-4974-A75E-F58EBCD98ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {098D9321-1201-4974-A75E-F58EBCD98ACF}.Release|Any CPU.Build.0 = Release|Any CPU + {1BFF54F9-4E35-49DB-893C-AF2047722FC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BFF54F9-4E35-49DB-893C-AF2047722FC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BFF54F9-4E35-49DB-893C-AF2047722FC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BFF54F9-4E35-49DB-893C-AF2047722FC6}.Release|Any CPU.Build.0 = Release|Any CPU + {53AF2D01-B69F-4CD0-86A7-8FD95967D23C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53AF2D01-B69F-4CD0-86A7-8FD95967D23C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53AF2D01-B69F-4CD0-86A7-8FD95967D23C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53AF2D01-B69F-4CD0-86A7-8FD95967D23C}.Release|Any CPU.Build.0 = Release|Any CPU + {78E15EC1-7732-41E3-8591-934E9F583254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78E15EC1-7732-41E3-8591-934E9F583254}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78E15EC1-7732-41E3-8591-934E9F583254}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78E15EC1-7732-41E3-8591-934E9F583254}.Release|Any CPU.Build.0 = Release|Any CPU + {CB1EE94E-CB83-4071-9DD0-9929AE2B7282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB1EE94E-CB83-4071-9DD0-9929AE2B7282}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB1EE94E-CB83-4071-9DD0-9929AE2B7282}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB1EE94E-CB83-4071-9DD0-9929AE2B7282}.Release|Any CPU.Build.0 = Release|Any CPU + {658EF9BE-452C-4D31-AA24-B9E2235799A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {658EF9BE-452C-4D31-AA24-B9E2235799A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {658EF9BE-452C-4D31-AA24-B9E2235799A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {658EF9BE-452C-4D31-AA24-B9E2235799A8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {098D9321-1201-4974-A75E-F58EBCD98ACF} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F} + {1BFF54F9-4E35-49DB-893C-AF2047722FC6} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7} + {53AF2D01-B69F-4CD0-86A7-8FD95967D23C} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F} + {78E15EC1-7732-41E3-8591-934E9F583254} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7} + {CB1EE94E-CB83-4071-9DD0-9929AE2B7282} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7} + {658EF9BE-452C-4D31-AA24-B9E2235799A8} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B526D2CE-EE2D-4AD4-93EF-1867D90FF1F5} + EndGlobalSection +EndGlobal diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 000000000..a35c3d36f --- /dev/null +++ b/NuGet.config @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PULL_REQUEST_TEMPLATE b/PULL_REQUEST_TEMPLATE new file mode 100644 index 000000000..cf58398a0 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE @@ -0,0 +1,2 @@ +- Please add description for changes you are making. +- If there is an issue related to this PR, please add the reference. diff --git a/README.md b/README.md new file mode 100644 index 000000000..2c78ac7ea --- /dev/null +++ b/README.md @@ -0,0 +1,176 @@ +# .NET SDK Installers + +[![GitHub release](https://img.shields.io/github/release/dotnet/installer.svg)](https://GitHub.com/dotnet/installer/releases/) +[![GitHub repo size](https://img.shields.io/github/repo-size/dotnet/installer)](https://github.com/dotnet/installer) +[![GitHub issues-opened](https://img.shields.io/github/issues/dotnet/installer.svg)](https://GitHub.com/dotnet/installer/issues?q=is%3Aissue+is%3Aopened) +[![GitHub issues-closed](https://img.shields.io/github/issues-closed/dotnet/installer.svg)](https://GitHub.com/dotnet/installer/issues?q=is%3Aissue+is%3Aclosed) +[![GitHub pulls-opened](https://img.shields.io/github/issues-pr/dotnet/installer.svg)](https://GitHub.com/dotnet/installer/pulls?q=is%3Aissue+is%3Aopened) +[![GitHub pulls-merged](https://img.shields.io/github/issues-search/dotnet/installer?label=merged%20pull%20requests&query=is%3Apr%20is%3Aclosed%20is%3Amerged&color=darkviolet)](https://github.com/dotnet/installer/pulls?q=is%3Apr+is%3Aclosed+is%3Amerged) +[![GitHub pulls-unmerged](https://img.shields.io/github/issues-search/dotnet/installer?label=unmerged%20pull%20requests&query=is%3Apr%20is%3Aclosed%20is%3Aunmerged&color=red)](https://github.com/dotnet/installer/pulls?q=is%3Apr+is%3Aclosed+is%3Aunmerged) +[![GitHub contributors](https://img.shields.io/github/contributors/dotnet/installer.svg)](https://GitHub.com/dotnet/installer/graphs/contributors/) +[![Commit Activity](https://img.shields.io/github/commit-activity/m/dotnet/installer)]() + +This repo contains the source code for the cross-platform [.NET](http://github.com/dotnet/core) SDK. It aggregates the .NET toolchain, the .NET runtime, the templates, and the .NET Windows Desktop runtime. It produces zip, tarballs, and native packages for various supported platforms. + +Looking for released versions of the .NET tooling? +---------------------------------------- + +The links below are for preview versions of .NET tooling. Prefer to use released versions of the .NET tools? Go to https://dot.net/download. + +Looking for .NET Framework downloads? +---------------------------------------- + +.NET Framework is the product from which the .NET Core project originated. .NET Core (mostly just called ".NET" here) adds many features and improvements and supports many more platforms than .NET Framework. .NET Framework remains fully supported and you can find the downloads on the [.NET website](https://dotnet.microsoft.com/download/dotnet-framework). For new projects, we recommend you use .NET Core. + +Want to contribute or find out more about the .NET project? +---------------------------------------- + +This repo is for the installers. Most of the implementation is in other repos, such as the [dotnet/runtime repo](https://github.com/dotnet/runtime) or the [dotnet/aspnetcore repo](https://github.com/dotnet/aspnetcore) and [many others](https://github.com/dotnet/core/blob/main/Documentation/core-repos.md). We welcome you to join us there! + +Found an issue? +--------------- +You can consult the [Documents Index for the SDK repo](https://github.com/dotnet/sdk/blob/main/documentation/README.md) to find out current issues, see workarounds, and to see how to file new issues. + +This project has adopted the code of conduct defined by the [Contributor Covenant](http://contributor-covenant.org/) to clarify expected behavior in our community. For more information, see the [.NET Foundation Code of Conduct](http://www.dotnetfoundation.org/code-of-conduct). + +# Build .NET installer + +The repository contains native code project required for the Windows installer. If you intend to build it locally on Windows, you will need to ensure that you have the following items installed. +- Install CMAKE 3.21.0 is required if you're building VS 17.0. Make sure to add CMAKE to your PATH (the installer will prompt you). +- Install MSVC Build tools for x86/x64/arm64, v14.28-16.9 + +- `build` for basic build +- `build -pack` to build the installer +- To build in VS, run a command line build first, then run `artifacts\core-sdk-build-env.bat` from a VS command prompt and then `devenv Microsoft.DotNet.Cli.sln` +- To test different languages of the installer, run `artifacts\packages\Debug\Shipping>dotnet-sdk-3.1.412-win-x64.exe /lang 1046` using the LCID of the language you want to test + +# Build .NET from source (source-build) + +Please see the [dotnet/source-build](https://github.com/dotnet/source-build) repo for more information. + +## Support + +.NET Source-Build is supported on the oldest available .NET SDK feature update for each major release, and on Linux only. +For example, if .NET 6.0.1xx, 6.0.2xx, and 7.0.1xx feature updates are available from [dotnet.microsoft.com](https://dotnet.microsoft.com/en-us/download/dotnet/6.0), Source-Build will support 6.0.1xx and 7.0.1xx. +For the latest information about Source-Build support for new .NET versions, please check our [GitHub Discussions page](https://github.com/dotnet/source-build/discussions) for announcements. + +## Building .NET 8.0 + +.NET 8.0 (currently in prerelease) and newer will be built from the [dotnet/dotnet](https://github.com/dotnet/dotnet) repo. +Clone the dotnet/dotnet repo and check out the tag for the desired release. +Then, follow the instructions in [dotnet/dotnet's README](https://github.com/dotnet/dotnet/blob/main/README.md#dev-instructions) to build .NET from source. + +## Building .NET 7.0 and .NET 6.0 + +1. Create a .NET source tarball. + + ```bash + ./build.sh /p:ArcadeBuildTarball=true /p:TarballDir=/path/to/place/complete/dotnet/sources + ``` + + This fetches the complete .NET source code and creates a tarball at `artifacts/packages//Shipping/`. + The extracted source code is also placed at `/path/to/place/complete/dotnet/sources`. + The source directory should be outside (and not somewhere under) the installer directory. + +2. Prep the source to build on your distro. This downloads a .NET SDK and a number of .NET packages needed to build .NET from source. + + ```bash + cd /path/to/complete/dotnet/sources + ./prep.sh --bootstrap + ``` + +3. Build the .NET SDK + + ```bash + ./build.sh --clean-while-building + ``` + + This builds the entire .NET SDK from source. + The resulting SDK is placed at `artifacts/x64/Release/dotnet-sdk-7.0.100-your-RID.tar.gz`. + + Optionally add the `--online` flag to add online NuGet restore sources to the build. + This is useful for testing unsupported releases that don't yet build without downloading pre-built binaries from the internet. + + Run `./build.sh --help` to see more information about supported build options. + +4. (Optional) Unpack and install the .NET SDK + + ```bash + mkdir -p $HOME/dotnet + tar zxf artifacts/x64/Release/dotnet-sdk-7.0.100-your-RID.tar.gz -C $HOME/dotnet + ln -s $HOME/dotnet/dotnet /usr/bin/dotnet + ``` + + To test your source-built SDK, run the following: + + ```bash + dotnet --info + ``` + +# Build status + +Visibility|All legs| +|:------|:------| +|Public|[![Status](https://dev.azure.com/dnceng-public/public/_apis/build/status%2Fdotnet%2Finstaller%2Finstaller?branchName=main)](https://dev.azure.com/dnceng-public/public/_build/latest?definitionId=20&branchName=main)| +|Microsoft Internal|[![Status](https://dev.azure.com/dnceng/internal/_apis/build/status/286)](https://dev.azure.com/dnceng/internal/_build?definitionId=286)| + +## Installers and Binaries + +You can download the .NET SDK as either an installer (MSI, PKG) or a zip (zip, tar.gz). The .NET SDK contains both the .NET runtime and CLI tools. + +**Note:** Be aware that the following installers are the **latest bits**. If you +want to install the latest released versions, check out the [preceding section](#looking-for-released-versions-of-the-net-core-tooling). +With development builds, internal NuGet feeds are necessary for some scenarios (for example, to acquire the runtime pack for self-contained apps). You can use the following NuGet.config to configure these feeds. See the following document [Configuring NuGet behavior](https://docs.microsoft.com/en-us/nuget/consume-packages/configuring-nuget-behavior) for more information on where to modify your NuGet.config to apply the changes. + +**For .NET 9 builds** + +```xml + + + + + +``` + +**For .NET 8 builds** + +```xml + + + + + +``` + +**For .NET 7 builds** + +```xml + + + + + +``` + +Do not directly edit the table below. Use https://github.com/dotnet/installer/tree/main/tools/sdk-readme-table-generator to help you generate it. Make sure to run the table generator test and make any changes to the generator along with your changes to the table. Daily servicing builds aren't shown here because they may contain upcoming security fixes. All public servicing builds can be downloaded at http://aka.ms/dotnet-download. + +### Table + +Latest builds table is in [here](https://github.com/dotnet/sdk/blob/main/documentation/package-table.md#table). + +Looking for dotnet-install sources? +----------------------------------- + +Sources for dotnet-install.sh and dotnet-install.ps1 are in the [install-scripts repo](https://github.com/dotnet/install-scripts). + +Questions & Comments +-------------------- + +For all feedback, use the Issues on the [.NET SDK](https://github.com/dotnet/sdk) repository. + +License +------- + +The .NET project uses the [MIT license](LICENSE). + +The LICENSE and ThirdPartyNotices in any downloaded archives are authoritative. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..030ac0c06 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +The .NET Core and ASP.NET Core support policy, including supported versions can be found at the [.NET Core Support Policy Page](https://dotnet.microsoft.com/platform/support/policy/dotnet-core). + +## Reporting a Vulnerability + +Security issues and bugs should be reported privately to the Microsoft Security Response Center (MSRC), either by emailing secure@microsoft.com or via the portal at https://msrc.microsoft.com. +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your +original message. Further information, including the MSRC PGP key, can be found in the [MSRC Report an Issue FAQ](https://www.microsoft.com/en-us/msrc/faqs-report-an-issue). + +Reports via MSRC may qualify for the .NET Core Bug Bounty. Details of the .NET Core Bug Bounty including terms and conditions are at [https://aka.ms/corebounty](https://aka.ms/corebounty). + +Please do not open issues for anything you think might have a security implication. \ No newline at end of file diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES new file mode 100644 index 000000000..67692ee13 --- /dev/null +++ b/THIRD-PARTY-NOTICES @@ -0,0 +1,112 @@ +.NET Core uses third-party libraries or other resources that may be +distributed under licenses different than the .NET Core software. + +Attributions and licence notices for test cases originally authored by +third parties can be found in the respective test directories. + +In the event that we accidentally failed to list a required notice, please +bring it to our attention. Post an issue or email us: + + dotnet@microsoft.com + +The attached notices are provided for information only. + +License notice for Nuget.Client +------------------------------- + +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + + +License notice for LZMA SDK +--------------------------- + +http://7-zip.org/sdk.html + +LZMA SDK is placed in the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute the +original LZMA SDK code, either in source code form or as a compiled binary, +for any purpose, commercial or non-commercial, and by any means. + +License notice for RFC 3492 +--------------------------- + +Copyright (C) The Internet Society (2003). All Rights Reserved. + +This document and translations of it may be copied and furnished to +others, and derivative works that comment on or otherwise explain it +or assist in its implementation may be prepared, copied, published +and distributed, in whole or in part, without restriction of any +kind, provided that the above copyright notice and this paragraph are +included on all such copies and derivative works. However, this +document itself may not be modified in any way, such as by removing +the copyright notice or references to the Internet Society or other +Internet organizations, except as needed for the purpose of +developing Internet standards in which case the procedures for +copyrights defined in the Internet Standards process must be +followed, or as required to translate it into languages other than +English. + +The limited permissions granted above are perpetual and will not be +revoked by the Internet Society or its successors or assigns. + +This document and the information contained herein is provided on an +"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING +TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION +HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +License notice for MonoDevelop +------------------------------ + +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Copyright (c) 2011 Novell, Inc (http://www.novell.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/MultiTFMTestApp.csproj b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/MultiTFMTestApp.csproj new file mode 100644 index 000000000..42170c6bb --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/MultiTFMTestApp.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.2;net461 + + + + + + + diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/Program.cs b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/Program.cs new file mode 100644 index 000000000..a168da37c --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/Program.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace TestApp +{ + class Program + { + public static void Main(string[] args) + { + Console.WriteLine(TestLibrary.Helper.GetMessage()); + } + } +} diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestApp/Program.cs b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestApp/Program.cs new file mode 100644 index 000000000..a168da37c --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestApp/Program.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace TestApp +{ + class Program + { + public static void Main(string[] args) + { + Console.WriteLine(TestLibrary.Helper.GetMessage()); + } + } +} diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestApp/TestApp.csproj b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestApp/TestApp.csproj new file mode 100644 index 000000000..4f3707aba --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestApp/TestApp.csproj @@ -0,0 +1,12 @@ + + + + Exe + net461 + + + + + + + diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestLibrary/Helper.cs b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestLibrary/Helper.cs new file mode 100644 index 000000000..67b32cfdb --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestLibrary/Helper.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace TestLibrary +{ + public static class Helper + { + /// + /// Gets the message from the helper. This comment is here to help test XML documentation file generation, please do not remove it. + /// + /// A message + public static string GetMessage() + { + return "This string came from the test library!"; + } + + public static void SayHi() + { + Console.WriteLine("Hello there!"); + } + } +} \ No newline at end of file diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestLibrary/TestLibrary.csproj b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestLibrary/TestLibrary.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/TestLibrary/TestLibrary.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/Directory.Build.props b/TestAssets/Directory.Build.props new file mode 100644 index 000000000..905549edc --- /dev/null +++ b/TestAssets/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/TestAssets/Directory.Build.targets b/TestAssets/Directory.Build.targets new file mode 100644 index 000000000..5cc7cd86a --- /dev/null +++ b/TestAssets/Directory.Build.targets @@ -0,0 +1,3 @@ + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Empty/README b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Empty/README new file mode 100644 index 000000000..439cfe9c7 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Empty/README @@ -0,0 +1,2 @@ +This directory is intentionally empty. + diff --git a/TestAssets/TestPackages/dotnet-portable/Program.cs b/TestAssets/TestPackages/dotnet-portable/Program.cs new file mode 100644 index 000000000..02d81ba5c --- /dev/null +++ b/TestAssets/TestPackages/dotnet-portable/Program.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Reflection; + +namespace ConsoleApplication +{ + public class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello Portable World!"); + + var depsFile = new FileInfo(AppContext.GetData("FX_DEPS_FILE") as string); + string frameworkVersion = depsFile.Directory.Name; + + Console.WriteLine($"I'm running on shared framework version {frameworkVersion}!"); + } + } +} diff --git a/TestAssets/TestPackages/dotnet-portable/dotnet-portable.csproj b/TestAssets/TestPackages/dotnet-portable/dotnet-portable.csproj new file mode 100644 index 000000000..13267b228 --- /dev/null +++ b/TestAssets/TestPackages/dotnet-portable/dotnet-portable.csproj @@ -0,0 +1,8 @@ + + + + netcoreapp2.2 + Exe + + + diff --git a/TestAssets/TestPackages/dotnet-prefercliruntime/Program.cs b/TestAssets/TestPackages/dotnet-prefercliruntime/Program.cs new file mode 100644 index 000000000..d594c0619 --- /dev/null +++ b/TestAssets/TestPackages/dotnet-prefercliruntime/Program.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace ConsoleApplication +{ + public class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello I prefer the cli runtime World!"); + } + } +} diff --git a/TestAssets/TestPackages/dotnet-prefercliruntime/dotnet-prefercliruntime.csproj b/TestAssets/TestPackages/dotnet-prefercliruntime/dotnet-prefercliruntime.csproj new file mode 100644 index 000000000..366c75abe --- /dev/null +++ b/TestAssets/TestPackages/dotnet-prefercliruntime/dotnet-prefercliruntime.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.2 + Exe + + + + + + true + / + + + diff --git a/TestAssets/TestPackages/dotnet-prefercliruntime/prefercliruntime b/TestAssets/TestPackages/dotnet-prefercliruntime/prefercliruntime new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/AppWithApplicationUrlInLaunchSettings.csproj b/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/AppWithApplicationUrlInLaunchSettings.csproj new file mode 100644 index 000000000..44c603541 --- /dev/null +++ b/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/AppWithApplicationUrlInLaunchSettings.csproj @@ -0,0 +1,6 @@ + + + Exe + $(CliTargetFramework) + + diff --git a/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/Program.cs b/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/Program.cs new file mode 100644 index 000000000..33322e771 --- /dev/null +++ b/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/Program.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace MSBuildTestApp +{ + public class Program + { + public static void Main(string[] args) + { + var message = Environment.GetEnvironmentVariable("ASPNETCORE_URLS"); + Console.WriteLine(message); + } + } +} diff --git a/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/Properties/launchSettings.json b/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/Properties/launchSettings.json new file mode 100644 index 000000000..b61eabd57 --- /dev/null +++ b/TestAssets/TestProjects/AppWithApplicationUrlInLaunchSettings/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:49850/", + "sslPort": 0 + } + }, + "profiles": { + "First": { + "commandName": "Project", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:12345/" + }, + "applicationUrl": "http://localhost:67890/" + }, + "Second": { + "commandName": "Project", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:54321/" + } + } +} \ No newline at end of file diff --git a/TestAssets/TestProjects/MSBuildTestApp/MSBuildTestApp.csproj b/TestAssets/TestProjects/MSBuildTestApp/MSBuildTestApp.csproj new file mode 100644 index 000000000..b22ce46fd --- /dev/null +++ b/TestAssets/TestProjects/MSBuildTestApp/MSBuildTestApp.csproj @@ -0,0 +1,11 @@ + + + + Exe + net5.0 + + + + + + diff --git a/TestAssets/TestProjects/MSBuildTestApp/Program.cs b/TestAssets/TestProjects/MSBuildTestApp/Program.cs new file mode 100644 index 000000000..e50829e81 --- /dev/null +++ b/TestAssets/TestProjects/MSBuildTestApp/Program.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace MSBuildTestApp +{ + public class Program + { + public static void Main(string[] args) + { + if (args.Length > 0) + { + Console.WriteLine("echo args:" + string.Join(";", args)); + } + Console.WriteLine("Hello World!"); + } + } +} diff --git a/TestAssets/TestProjects/SlnFileWithNoProjectReferencesAndUnknownProjectType/App.sln b/TestAssets/TestProjects/SlnFileWithNoProjectReferencesAndUnknownProjectType/App.sln new file mode 100644 index 000000000..5b61df887 --- /dev/null +++ b/TestAssets/TestProjects/SlnFileWithNoProjectReferencesAndUnknownProjectType/App.sln @@ -0,0 +1,18 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TestAssets/TestProjects/SlnFileWithNoProjectReferencesAndUnknownProjectType/UnknownProject/UnknownProject.unknownproj b/TestAssets/TestProjects/SlnFileWithNoProjectReferencesAndUnknownProjectType/UnknownProject/UnknownProject.unknownproj new file mode 100644 index 000000000..937af7f18 --- /dev/null +++ b/TestAssets/TestProjects/SlnFileWithNoProjectReferencesAndUnknownProjectType/UnknownProject/UnknownProject.unknownproj @@ -0,0 +1,2 @@ + + diff --git a/TestAssets/TestProjects/TestAppSimple/Program.cs b/TestAssets/TestProjects/TestAppSimple/Program.cs new file mode 100644 index 000000000..d431e919c --- /dev/null +++ b/TestAssets/TestProjects/TestAppSimple/Program.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace ConsoleApplication +{ + public class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + + if (args.Length > 0) + { + for (int i = 0; i < args.Length; i++) + { + Console.WriteLine($"{i} = {args[i]}"); + } + } + } + } +} diff --git a/TestAssets/TestProjects/TestAppSimple/TestAppSimple.csproj b/TestAssets/TestProjects/TestAppSimple/TestAppSimple.csproj new file mode 100644 index 000000000..ff8d8501d --- /dev/null +++ b/TestAssets/TestProjects/TestAppSimple/TestAppSimple.csproj @@ -0,0 +1,6 @@ + + + netcoreapp2.2 + Exe + + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Empty/README b/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Empty/README new file mode 100644 index 000000000..439cfe9c7 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Empty/README @@ -0,0 +1,2 @@ +This directory is intentionally empty. + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Multiple/First.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Multiple/First.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Multiple/First.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Multiple/Second.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Multiple/Second.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojFiles/Multiple/Second.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Empty/README b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Empty/README new file mode 100644 index 000000000..439cfe9c7 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Empty/README @@ -0,0 +1,2 @@ +This directory is intentionally empty. + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Multiple/First.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Multiple/First.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Multiple/First.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Multiple/Second.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Multiple/Second.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Multiple/Second.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/App.sln b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/App.sln new file mode 100644 index 000000000..acf657a65 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/App.sln @@ -0,0 +1,21 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + Foo Bar|Any CPU = Foo Bar|Any CPU + Foo Bar|x64 = Foo Bar|x64 + Foo Bar|x86 = Foo Bar|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithAdditionalConfigs/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithAdditionalConfigs/Library.cs new file mode 100644 index 000000000..786c0221c --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithAdditionalConfigs/Library.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace ProjectWithAdditionalConfigs +{ + public static class Library + { + public static string GetMessage() + { + return "Hello World!"; + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithAdditionalConfigs/ProjectWithAdditionalConfigs.csproj b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithAdditionalConfigs/ProjectWithAdditionalConfigs.csproj new file mode 100644 index 000000000..9b87758e9 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithAdditionalConfigs/ProjectWithAdditionalConfigs.csproj @@ -0,0 +1,10 @@ + + + + netstandard2.0 + {A302325B-D680-4C0E-8680-7AE283981624} + AnyCPU;x64;x86;AdditionalPlatform + Debug;Release;FooBar;AdditionalConfiguration + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithMatchingConfigs/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithMatchingConfigs/Library.cs new file mode 100644 index 000000000..c0d7436b3 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithMatchingConfigs/Library.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace ProjectWithMatchingConfigs +{ + public static class Library + { + public static string GetMessage() + { + return "Hello World!"; + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithMatchingConfigs/ProjectWithMatchingConfigs.csproj b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithMatchingConfigs/ProjectWithMatchingConfigs.csproj new file mode 100644 index 000000000..7c789108f --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithMatchingConfigs/ProjectWithMatchingConfigs.csproj @@ -0,0 +1,10 @@ + + + + netstandard2.0 + {C9601CA2-DB64-4FB6-B463-368C7764BF0D} + AnyCPU;x64;x86 + Debug;Release;FooBar + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithoutMatchingConfigs/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithoutMatchingConfigs/Library.cs new file mode 100644 index 000000000..fb9a17c27 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithoutMatchingConfigs/Library.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace ProjectWithoutMatchingConfigs +{ + public static class Library + { + public static string GetMessage() + { + return "Hello World!"; + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithoutMatchingConfigs/ProjectWithoutMatchingConfigs.csproj b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithoutMatchingConfigs/ProjectWithoutMatchingConfigs.csproj new file mode 100644 index 000000000..4bc811fa1 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndProjectConfigs/ProjectWithoutMatchingConfigs/ProjectWithoutMatchingConfigs.csproj @@ -0,0 +1,8 @@ + + + + netstandard2.0 + {C49B64DE-4401-4825-8A88-10DCB5950E57} + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App.sln b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App.sln new file mode 100644 index 000000000..97c495544 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27110.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\App.csproj", "{BB02B949-F6BD-4872-95CB-96A05B1FE026}" + ProjectSection(ProjectDependencies) = postProject + {4E952B56-841D-461D-89A9-7977DDCC0625} = {4E952B56-841D-461D-89A9-7977DDCC0625} + {D53E177A-8ECF-43D5-A01E-98B884D53CA6} = {D53E177A-8ECF-43D5-A01E-98B884D53CA6} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "First", "First\First.csproj", "{D53E177A-8ECF-43D5-A01E-98B884D53CA6}" + ProjectSection(ProjectDependencies) = postProject + {4E952B56-841D-461D-89A9-7977DDCC0625} = {4E952B56-841D-461D-89A9-7977DDCC0625} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Second", "Second\Second.csproj", "{4E952B56-841D-461D-89A9-7977DDCC0625}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Release|Any CPU.Build.0 = Release|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Release|Any CPU.Build.0 = Release|Any CPU + {4E952B56-841D-461D-89A9-7977DDCC0625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E952B56-841D-461D-89A9-7977DDCC0625}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E952B56-841D-461D-89A9-7977DDCC0625}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E952B56-841D-461D-89A9-7977DDCC0625}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F6D9A973-1CFD-41C9-84F2-1471C0FE67DF} + EndGlobalSection +EndGlobal diff --git a/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App/App.csproj b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App/App.csproj new file mode 100644 index 000000000..ce1697ae8 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App/App.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp2.0 + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App/Program.cs b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App/Program.cs new file mode 100644 index 000000000..be4278310 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/App/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace App +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/First/Class1.cs b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/First/Class1.cs new file mode 100644 index 000000000..3a358f6f2 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/First/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace First +{ + public class Class1 + { + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/First/First.csproj b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/First/First.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/First/First.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/Second/Class1.cs b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/Second/Class1.cs new file mode 100644 index 000000000..a39ee63c8 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/Second/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace Second +{ + public class Class1 + { + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/Second/Second.csproj b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/Second/Second.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnProjectDependencyToRemove/Second/Second.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/TestProjects/TestRazorApp/Areas/MyFeature/Pages/Page1.cshtml b/TestAssets/TestProjects/TestRazorApp/Areas/MyFeature/Pages/Page1.cshtml new file mode 100644 index 000000000..79083e425 --- /dev/null +++ b/TestAssets/TestProjects/TestRazorApp/Areas/MyFeature/Pages/Page1.cshtml @@ -0,0 +1,16 @@ +@page +@model TestRazorApp.MyFeature.Pages.Page1Model +@{ + Layout = null; +} + + + + + + + Page1 + + + + diff --git a/TestAssets/TestProjects/TestRazorApp/Areas/MyFeature/Pages/Page1.cshtml.cs b/TestAssets/TestProjects/TestRazorApp/Areas/MyFeature/Pages/Page1.cshtml.cs new file mode 100644 index 000000000..583793ac3 --- /dev/null +++ b/TestAssets/TestProjects/TestRazorApp/Areas/MyFeature/Pages/Page1.cshtml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace TestRazorApp.MyFeature.Pages +{ + public class Page1Model : PageModel + { + public void OnGet() + { + + } + } +} \ No newline at end of file diff --git a/TestAssets/TestProjects/TestRazorApp/TestRazorApp.csproj b/TestAssets/TestProjects/TestRazorApp/TestRazorApp.csproj new file mode 100644 index 000000000..154ba500e --- /dev/null +++ b/TestAssets/TestProjects/TestRazorApp/TestRazorApp.csproj @@ -0,0 +1,9 @@ + + + netcoreapp3.0 + + + + + + diff --git a/TestAssets/TestProjects/TestWebAppSimple/Program.cs b/TestAssets/TestProjects/TestWebAppSimple/Program.cs new file mode 100644 index 000000000..a15982830 --- /dev/null +++ b/TestAssets/TestProjects/TestWebAppSimple/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace TestWebAppSimple +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/TestAssets/TestProjects/TestWebAppSimple/Startup.cs b/TestAssets/TestProjects/TestWebAppSimple/Startup.cs new file mode 100644 index 000000000..f00c04c75 --- /dev/null +++ b/TestAssets/TestProjects/TestWebAppSimple/Startup.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace TestWebAppSimple +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.Run(async (context) => + { + await context.Response.WriteAsync("Hello World!"); + }); + } + } +} diff --git a/TestAssets/TestProjects/TestWebAppSimple/TestWebAppSimple.csproj b/TestAssets/TestProjects/TestWebAppSimple/TestWebAppSimple.csproj new file mode 100644 index 000000000..382a97d4c --- /dev/null +++ b/TestAssets/TestProjects/TestWebAppSimple/TestWebAppSimple.csproj @@ -0,0 +1,28 @@ + + + $(CliTargetFramework) + + + + + + + + + + + + + + + + + + + diff --git a/TestAssets/TestProjects/UseCswinrt/Program.cs b/TestAssets/TestProjects/UseCswinrt/Program.cs new file mode 100644 index 000000000..890bc1e0e --- /dev/null +++ b/TestAssets/TestProjects/UseCswinrt/Program.cs @@ -0,0 +1,14 @@ +using System; +using Windows.Data.Json; + +namespace consolecswinrt +{ + class Program + { + static void Main(string[] args) + { + var rootObject = JsonObject.Parse("{\"greet\": \"Hello\"}"); + Console.WriteLine(rootObject["greet"]); + } + } +} diff --git a/TestAssets/TestProjects/UseCswinrt/consolecswinrt.csproj b/TestAssets/TestProjects/UseCswinrt/consolecswinrt.csproj new file mode 100644 index 000000000..6ed44942b --- /dev/null +++ b/TestAssets/TestProjects/UseCswinrt/consolecswinrt.csproj @@ -0,0 +1,7 @@ + + + Exe + net9.0 + Windows + + diff --git a/TestAssets/TestProjects/docker-compose/docker-compose.dcproj b/TestAssets/TestProjects/docker-compose/docker-compose.dcproj new file mode 100644 index 000000000..2ab9fb72f --- /dev/null +++ b/TestAssets/TestProjects/docker-compose/docker-compose.dcproj @@ -0,0 +1,12 @@ + + + 2.0 + Linux + LaunchBrowser + http://localhost:{ServicePort} + testwebapplication + + + + + \ No newline at end of file diff --git a/TestAssets/TestProjects/docker-compose/docker-compose.yml b/TestAssets/TestProjects/docker-compose/docker-compose.yml new file mode 100644 index 000000000..9903b7748 --- /dev/null +++ b/TestAssets/TestProjects/docker-compose/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3.0' + +services: + testwebapplication: + image: testwebapplication + build: + context: . + dockerfile: testwebapplication/Dockerfile diff --git a/build.cmd b/build.cmd new file mode 100644 index 000000000..ce681438b --- /dev/null +++ b/build.cmd @@ -0,0 +1,7 @@ +@echo off + +REM Copyright (c) .NET Foundation and contributors. All rights reserved. +REM Licensed under the MIT license. See LICENSE file in the project root for full license information. + +powershell -ExecutionPolicy Bypass -NoProfile -NoLogo -Command "& \"%~dp0run-build.ps1\" %*; exit $LastExitCode;" +if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..d65f55854 --- /dev/null +++ b/build.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +# Set OFFLINE environment variable to build offline + +set -e + +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done +DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + +# Some things depend on HOME and it may not be set. We should fix those things, but until then, we just patch a value in +if [ -z "$HOME" ]; then + export HOME=$DIR/artifacts/home + + [ ! -d "$HOME" ] || rm -Rf $HOME + mkdir -p $HOME +fi + +args= + +while [[ $# > 0 ]]; do + lowerI="$(echo $1 | awk '{print tolower($0)}')" + case $lowerI in + --noprettyprint) + export DOTNET_CORESDK_NOPRETTYPRINT=1 + ;; + *) + args="$args $1" + ;; + esac + shift +done + +$DIR/run-build.sh $args diff --git a/eng/AfterSigning.targets b/eng/AfterSigning.targets new file mode 100644 index 000000000..8e7aa3cf4 --- /dev/null +++ b/eng/AfterSigning.targets @@ -0,0 +1,23 @@ + + + + <_SuppressSdkImports>false + + + + + + + + + + + + %(FullPath).sha512 + + + + + + + diff --git a/eng/Build.props b/eng/Build.props new file mode 100644 index 000000000..fb03e7d3c --- /dev/null +++ b/eng/Build.props @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/eng/BuildConfiguration/build-configuration.json b/eng/BuildConfiguration/build-configuration.json new file mode 100644 index 000000000..34c6ade74 --- /dev/null +++ b/eng/BuildConfiguration/build-configuration.json @@ -0,0 +1,10 @@ +{ + "RetryCountLimit":1, + "RetryByPipeline":{ + "RetryStages":[ + { + "StageName":"build" + } + ] + } +} \ No newline at end of file diff --git a/eng/CopyToLatest.cmd b/eng/CopyToLatest.cmd new file mode 100644 index 000000000..67b0a5860 --- /dev/null +++ b/eng/CopyToLatest.cmd @@ -0,0 +1,7 @@ +@echo off + +REM Copyright (c) .NET Foundation and contributors. All rights reserved. +REM Licensed under the MIT license. See LICENSE file in the project root for full license information. + +powershell -ExecutionPolicy Bypass -NoProfile -NoLogo -Command "& \"%~dp0common\build.ps1\" -restore -build /p:Projects=\"%~dp0..\src\CopyToLatest\CopyToLatest.csproj\" %*; exit $LastExitCode;" +if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/eng/Key.snk b/eng/Key.snk new file mode 100644 index 000000000..e10e4889c Binary files /dev/null and b/eng/Key.snk differ diff --git a/eng/ManualVersions.props b/eng/ManualVersions.props new file mode 100644 index 000000000..ce3306b86 --- /dev/null +++ b/eng/ManualVersions.props @@ -0,0 +1,20 @@ + + + + + 10.0.17763.31 + 10.0.18362.31 + 10.0.19041.31 + 10.0.20348.31 + 10.0.22000.31 + 10.0.22621.31 + + + diff --git a/eng/Publishing.props b/eng/Publishing.props new file mode 100644 index 000000000..753393a0c --- /dev/null +++ b/eng/Publishing.props @@ -0,0 +1,156 @@ + + + + Sdk + $(Product) + true + + false + + + + + $(SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) + + + $(CollectionUri.Split('/')[3]) + + + $(CollectionUri.Split('.')[0].Split('/')[2]) + + + + + + + + + + + + + + + + + $(AGENT_OS) + $(OS) + $(AssetManifestOS)-$(AGENT_JOBNAME) + $(AssetManifestOS) + $(AssetManifestOS)-$(Architecture) + $(BaseAssetManifestFileName)-installers + + $(ArtifactsLogDir)AssetManifest\$(InstallersAssetManifestFileName).xml + + $(ArtifactsDir)..\DotnetAssetsTmpDir\$([System.Guid]::NewGuid()) + $(ArtifactsDir)..\ChecksumAssetsTmpDir\$([System.Guid]::NewGuid()) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(BlobStoragePartialRelativePath)/$(FullNugetVersion)/$([System.String]::Copy('%(Filename)%(Extension)')) + + + + $(BlobStoragePartialRelativePath)/$(FullNugetVersion)/$([System.String]::Copy('%(Filename)%(Extension)')) + NonShipping=true + + + + $(BlobStoragePartialRelativePath)/$(FullNugetVersion)/$([System.String]::Copy('%(Filename)%(Extension)')) + + + + + + + + + + + + + + + false + true + + + + + + diff --git a/eng/SignCheckExclusionsFile.txt b/eng/SignCheckExclusionsFile.txt new file mode 100644 index 000000000..ed889da10 --- /dev/null +++ b/eng/SignCheckExclusionsFile.txt @@ -0,0 +1,8 @@ +;; Exclusions for SignCheck. +;; Format: https://github.com/dotnet/arcade/blob/397316e195639450b6c76bfeb9823b40bee72d6d/src/SignCheck/Microsoft.SignCheck/Verification/Exclusion.cs#L23-L35 + +*.js;; We do not sign JavaScript files. +*apphost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*singlefilehost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*comhost.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*apphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 diff --git a/eng/Signing.props b/eng/Signing.props new file mode 100644 index 000000000..448c47e2e --- /dev/null +++ b/eng/Signing.props @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + 3PartySHA2 + MicrosoftDotNet500 + + + true + true + + true + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/TestVersions.props b/eng/TestVersions.props new file mode 100644 index 000000000..cbee8ebb3 --- /dev/null +++ b/eng/TestVersions.props @@ -0,0 +1,8 @@ + + + 5.3.0 + $(MicrosoftDotnetToolsetInternalPackageVersion) + 15.8.0 + 1.3.1 + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml new file mode 100644 index 000000000..b0d9a4157 --- /dev/null +++ b/eng/Version.Details.xml @@ -0,0 +1,208 @@ + + + + https://github.com/dotnet/windowsdesktop + c14643d9ce8c64868af61b776bb4768123bcc6c2 + + + https://github.com/dotnet/windowsdesktop + c14643d9ce8c64868af61b776bb4768123bcc6c2 + + + https://github.com/dotnet/windowsdesktop + c14643d9ce8c64868af61b776bb4768123bcc6c2 + + + https://github.com/dotnet/windowsdesktop + c14643d9ce8c64868af61b776bb4768123bcc6c2 + + + https://github.com/dotnet/runtime + 5474ab5c50fc639e780e0322cd500b2e43ee9b0d + + + https://github.com/dotnet/runtime + 5474ab5c50fc639e780e0322cd500b2e43ee9b0d + + + https://github.com/dotnet/runtime + 5474ab5c50fc639e780e0322cd500b2e43ee9b0d + + + https://github.com/dotnet/runtime + 5474ab5c50fc639e780e0322cd500b2e43ee9b0d + + + https://github.com/dotnet/runtime + 5474ab5c50fc639e780e0322cd500b2e43ee9b0d + + + + + https://github.com/dotnet/core-setup + 7d57652f33493fa022125b7f63aad0d70c52d810 + + + https://github.com/dotnet/runtime + 5474ab5c50fc639e780e0322cd500b2e43ee9b0d + + + https://github.com/dotnet/aspnetcore + e577ff3a06b552e287fdf652b1cbaf04de3ba6f3 + + + https://github.com/dotnet/aspnetcore + e577ff3a06b552e287fdf652b1cbaf04de3ba6f3 + + + https://github.com/dotnet/aspnetcore + e577ff3a06b552e287fdf652b1cbaf04de3ba6f3 + + + https://github.com/dotnet/aspnetcore + e577ff3a06b552e287fdf652b1cbaf04de3ba6f3 + + + https://github.com/dotnet/aspnetcore + e577ff3a06b552e287fdf652b1cbaf04de3ba6f3 + + + https://github.com/dotnet/aspnetcore + e577ff3a06b552e287fdf652b1cbaf04de3ba6f3 + + + https://github.com/dotnet/aspnetcore + e577ff3a06b552e287fdf652b1cbaf04de3ba6f3 + + + https://github.com/dotnet/sdk + ace21a69999eede779921900fca9420d37395aee + + + https://github.com/dotnet/sdk + ace21a69999eede779921900fca9420d37395aee + + + https://github.com/dotnet/sdk + ace21a69999eede779921900fca9420d37395aee + + + https://github.com/dotnet/sdk + ace21a69999eede779921900fca9420d37395aee + + + https://github.com/dotnet/test-templates + 0385265f4d0b6413d64aea0223172366a9b9858c + + + https://github.com/dotnet/test-templates + 307b8f538d83a955d8f6dd909eee41a5555f2f4d + + + https://github.com/dotnet/test-templates + becc4bd157cd6608b51a5ffe414a5d2de6330272 + + + https://github.com/dotnet/test-templates + becc4bd157cd6608b51a5ffe414a5d2de6330272 + + + https://github.com/dotnet/test-templates + 49c9ad01f057b3c6352bbec12b117acc2224493c + + + https://github.com/dotnet/test-templates + f57e2c3eb78ac1033d410fde85509100b7512730 + + + + https://github.com/dotnet/winforms + 0aa3a4dfea1dad493ecbfe15cd513604468b3916 + + + https://github.com/dotnet/wpf + d6b1f4560cb72806a9b55beae86ee5f769ec0960 + + + https://github.com/dotnet/fsharp + 7d176e9b00021c50338c69d93ba8d326c65db1f1 + + + https://github.com/microsoft/vstest + 2588f022c1c4a12e159e0ed07f5a5ea3f3c9eaa8 + + + https://github.com/dotnet/runtime + 5474ab5c50fc639e780e0322cd500b2e43ee9b0d + + + https://github.com/dotnet/roslyn + 8d7a17f545de7fe5e37dc0e754bab0fbee1e4713 + + + https://github.com/dotnet/msbuild + 66e0371a64e08160e63000fc2ced8cb8bbc6739e + + + https://github.com/nuget/nuget.client + f702fc3dc44e17a0666dea01e9aa18c5c1175b27 + + + https://github.com/dotnet/emsdk + 53c4a109e2abc0112996e2bc4a7f830f572f4efd + + + https://github.com/dotnet/deployment-tools + 6b80783f6743ee9f18940eb6acb7135e5c111d4b + + + https://github.com/dotnet/command-line-api + 963d34b1fb712c673bfb198133d7e988182c9ef4 + + + + + https://github.com/dotnet/arcade + 1cf3eaa1f6ada43ab988145a3f3efddb1ffa3b10 + + + https://github.com/dotnet/arcade + 1cf3eaa1f6ada43ab988145a3f3efddb1ffa3b10 + + + https://github.com/dotnet/arcade + 1cf3eaa1f6ada43ab988145a3f3efddb1ffa3b10 + + + https://github.com/dotnet/arcade-services + 1d98f4c0a5b25b72465fe075dd5f24b45ef15c8e + + + https://github.com/dotnet/arcade-services + 1d98f4c0a5b25b72465fe075dd5f24b45ef15c8e + + + https://github.com/dotnet/arcade + 1cf3eaa1f6ada43ab988145a3f3efddb1ffa3b10 + + + https://github.com/dotnet/runtime + ab1a8224cdf115b65e0db5dc88d11f205068f444 + + + https://github.com/dotnet/scenario-tests + 259edc6efe049ed49f9e37890be702a886ba5ed8 + + + + https://github.com/dotnet/aspire + 0514ea9e12ece4dd764824ce925ae0eae6fcbd86 + + + diff --git a/eng/Versions.props b/eng/Versions.props new file mode 100644 index 000000000..6dd5ea97f --- /dev/null +++ b/eng/Versions.props @@ -0,0 +1,234 @@ + + + 9 + 0 + 1 + 00 + + $(VersionMajor).$(VersionMinor).$(VersionSDKMinor)00 + $(VersionMajor).$(VersionMinor).$(VersionSDKMinor)$(VersionFeature) + $(VersionMajor).$(VersionMinor) + $(MajorMinorVersion).$(VersionSDKMinor) + + false + release + + preview + rtm + servicing + 5 + + + 30 + 32 + 17 + 30 + 19 + 5 + + <_NET70ILLinkPackVersion>7.0.100-1.23401.1 + + + + + https://dotnetbuilds.blob.core.windows.net/public/ + https://dotnetclimsrc.blob.core.windows.net/dotnet/ + + + + 9.0.0-beta.24270.3 + + + + 1.1.0-beta.24256.1 + + + + 9.0.0-preview.5.24268.1 + + + + 9.0.0-preview.5.24270.11 + + + + + 1.1.0-rc.24069.1 + 1.1.0-rc.24202.1 + 1.1.0-rc.24270.2 + + + + 1.8.1 + + + + 9.0.0-preview.5.24270.27 + 9.0.0-preview.5.24270.27 + 9.0.0-preview.5.24270.27 + 9.0.0-preview.5.24270.27 + 9.0.0-preview.5.24270.27 + 9.0.0-preview.5.24270.27 + 9.0.0-preview.5.24270.27 + + + 0.2.0 + + + + 9.0.100-preview.5.24270.94 + 9.0.100-preview.5.24270.94 + 9.0.100-preview.5.24270.94 + $(MicrosoftNETSdkPackageVersion) + $(MicrosoftNETSdkPackageVersion) + $(MicrosoftNETSdkPackageVersion) + + + + 4.11.0-2.24270.11 + + + + 9.0.0-preview.5.24268.2 + + + + 9.0.0-preview.5.24268.2 + 9.0.0-preview.5.24268.2 + 9.0.0-preview.5.24268.2 + 9.0.0-preview.5.24268.2 + 9.0.0-preview.5.24268.2 + 2.1.0 + + + + 9.0.0-preview.5.24270.18 + 9.0.0-preview.5.24270.18 + 9.0.0-preview.5.24270.18 + 9.0.0-preview.5.24270.18 + + + + $(MicrosoftNETCoreAppHostwinx64PackageVersion) + $(MicrosoftNETCoreAppRuntimewinx64PackageVersion) + $(MicrosoftAspNetCoreAppRuntimewinx64PackageVersion) + $(MicrosoftWindowsDesktopAppRuntimewinx64PackageVersion) + + + + 6.11.0-preview.2.55 + + + + 2.0.0-preview.1.24176.3 + + + + + $(MicrosoftNETSdkPackageVersion.Split('.')[2]) + $(MicrosoftNETSdkFeatureAndPatchVersion.Split('-')[0]) + $(MicrosoftNETSdkFeatureAndPatchVersion.Substring(1)) + + true + true + true + $([MSBuild]::Subtract($(VersionFeature60), 1)) + $([MSBuild]::Subtract($(VersionFeature80), 1)) + $(VersionFeature60) + $(VersionFeature80) + $([MSBuild]::Subtract($(AspNetCoreTemplateFeature60), 1)) + $([MSBuild]::Subtract($(AspNetCoreTemplateFeature80), 1)) + $(VersionFeature80) + + + + 6.0.14 + 6.0.302 + 8.0.100 + 6.0.7-servicing.22322.3 + 8.0.0-rtm.23531.5 + 6.0.7-servicing.22322.2 + 8.0.0-rtm.23531.4 + + + $(MicrosoftNETCoreAppRuntimePackageVersion) + $(MicrosoftNETCoreAppRuntimePackageVersion) + + + + 3.14.1.8722 + + + + $(MicrosoftAspNetCoreAppRuntimePackageVersion) + $(MicrosoftDotNetCommonItemTemplatesPackageVersion) + $(MicrosoftDotNetCommonItemTemplatesPackageVersion) + $(MicrosoftDotnetWinFormsProjectTemplatesPackageVersion) + $(MicrosoftDotNetWpfProjectTemplatesPackageVersion) + $(NUnit3DotNetNewTemplatePackageVersion) + + 8.0.$(AspNetCoreTemplateFeature80) + $(MicrosoftDotNetCommonItemTemplates80PackageVersion) + $(MicrosoftWinFormsProjectTemplates80PackageVersion) + $(MicrosoftWPFProjectTemplates80PackageVersion) + $(NUnit3DotNetNewTemplatePackageVersion) + + 6.0.$(AspNetCoreTemplateFeature60) + $(MicrosoftDotNetCommonItemTemplates60PackageVersion) + $(MicrosoftDotNetCommonItemTemplates60PackageVersion) + $(MicrosoftWinFormsProjectTemplates60PackageVersion) + $(MicrosoftWPFProjectTemplates60PackageVersion) + $(NUnit3DotNetNewTemplatePackageVersion) + + + + 2.2.0-beta.19072.10 + 2.0.0 + 17.11.0-preview-24265-01 + 9.0.0-alpha.1.24058.10 + 9.0.0-preview.24257.1 + + + + 9.0.100-preview.1 + 9.0.0-preview.4.24229.9 + 9.0.100-preview.1 + 9.0.0-preview.1.9973 + 34.99.0-preview.1.151 + 17.2.9088-net9-p1 + 17.2.9088-net9-p1 + 14.2.9088-net9-p1 + 17.2.9088-net9-p1 + + 9.0.0-preview.5.24264.2 + $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportPackageVersion) + + 9.0.100$([System.Text.RegularExpressions.Regex]::Match($(EmscriptenWorkloadManifestVersion), `-[A-z]*[\.]*\d*`)) + + $(MicrosoftNETCoreAppRefPackageVersion) + + 9.0.100$([System.Text.RegularExpressions.Regex]::Match($(MonoWorkloadManifestVersion), `-[A-z]*[\.]*\d*`)) + + + + + 15.7.179 + 15.7.179 + + + + 2.0.1-servicing-26011-01 + + + diff --git a/eng/_prettyprint.sh b/eng/_prettyprint.sh new file mode 100644 index 000000000..a77002dd5 --- /dev/null +++ b/eng/_prettyprint.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +# Detect build servers +if [[ ! -z "$JENKINS_URL" || ! -z "$BUILD_BUILDID" || ! -z "$DOTNET_CORESDK_NOPRETTYPRINT" ]]; then + # Jenkins or VSO build, disable colors because they make things gross. + NO_COLOR=1 +fi + +if [ "$NO_COLOR" != "1" ]; then + # ANSI Colors + RCol='\e[0m' # Text Reset + + # Regular Bold Underline High Intensity BoldHigh Intens Background High Intensity Backgrounds + Bla='\e[0;30m'; BBla='\e[1;30m'; UBla='\e[4;30m'; IBla='\e[0;90m'; BIBla='\e[1;90m'; On_Bla='\e[40m'; On_IBla='\e[0;100m'; + Red='\e[0;31m'; BRed='\e[1;31m'; URed='\e[4;31m'; IRed='\e[0;91m'; BIRed='\e[1;91m'; On_Red='\e[41m'; On_IRed='\e[0;101m'; + Gre='\e[0;32m'; BGre='\e[1;32m'; UGre='\e[4;32m'; IGre='\e[0;92m'; BIGre='\e[1;92m'; On_Gre='\e[42m'; On_IGre='\e[0;102m'; + Yel='\e[0;33m'; BYel='\e[1;33m'; UYel='\e[4;33m'; IYel='\e[0;93m'; BIYel='\e[1;93m'; On_Yel='\e[43m'; On_IYel='\e[0;103m'; + Blu='\e[0;34m'; BBlu='\e[1;34m'; UBlu='\e[4;34m'; IBlu='\e[0;94m'; BIBlu='\e[1;94m'; On_Blu='\e[44m'; On_IBlu='\e[0;104m'; + Pur='\e[0;35m'; BPur='\e[1;35m'; UPur='\e[4;35m'; IPur='\e[0;95m'; BIPur='\e[1;95m'; On_Pur='\e[45m'; On_IPur='\e[0;105m'; + Cya='\e[0;36m'; BCya='\e[1;36m'; UCya='\e[4;36m'; ICya='\e[0;96m'; BICya='\e[1;96m'; On_Cya='\e[46m'; On_ICya='\e[0;106m'; + Whi='\e[0;37m'; BWhi='\e[1;37m'; UWhi='\e[4;37m'; IWhi='\e[0;97m'; BIWhi='\e[1;97m'; On_Whi='\e[47m'; On_IWhi='\e[0;107m'; +fi + +cecho() +{ + local text=$1 + printf "%b\n" "$text" +} + +header() +{ + local text=$1 + cecho "${BGre}*** $text ***${RCol}" +} + +info() +{ + local text=$1 + cecho "${Gre}info :${RCol} $text" +} + +warning() +{ + local text=$1 + cecho "${Yel}warn :${RCol} $text" 1>&2 +} + +error() +{ + local text=$1 + cecho "${Red}error:${RCol} $text" 1>&2 +} + +die() +{ + local text=$1 + error "$text" + exit 1 +} \ No newline at end of file diff --git a/eng/build-pr.yml b/eng/build-pr.yml new file mode 100644 index 000000000..9552a916c --- /dev/null +++ b/eng/build-pr.yml @@ -0,0 +1,261 @@ +parameters: + # Agent OS identifier and used as job name +- name: agentOs + type: string + + # Job name +- name: jobName + type: string + +# Container to run the build in, if any +- name: container + type: string + default: '' + + # Job timeout +- name: timeoutInMinutes + type: number + default: 180 + +# Build configuration (Debug, Release) +- name: buildConfiguration + type: string + values: + - Debug + - Release + +# Build architecture +- name: buildArchitecture + type: string + values: + - arm + - arm64 + - x64 + - x86 + +# Linux portable. If true, passes portable switch to build +- name: linuxPortable + type: boolean + default: false + +# Runtime Identifier +- name: runtimeIdentifier + type: string + default: '' + +# UI lang +- name: dotnetCLIUILanguage + type: string + default: '' + +# Additional parameters +- name: additionalBuildParameters + type: string + default: '' + +# Run tests +- name: runTests + type: boolean + default: true + +# PGO instrumentation jobs +- name: pgoInstrument + type: boolean + default: false + +- name: isBuiltFromVmr + displayName: True when build is running from dotnet/dotnet + type: boolean + default: false + +jobs: +- template: common/templates/job/job.yml + parameters: + # Set up the name of the job. + ${{ if parameters.pgoInstrument }}: + name: PGO_${{ parameters.agentOs }}_${{ parameters.jobName }} + ${{ if not(parameters.pgoInstrument) }}: + name: ${{ parameters.agentOs }}_${{ parameters.jobName }} + + ## Set up the pool/machine info to be used based on the Agent OS + ${{ if eq(parameters.agentOs, 'Windows_NT') }}: + enableMicrobuild: true + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64.open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + ${{ if eq(parameters.agentOs, 'Linux') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64 + container: ${{ parameters.container }} + ${{ if eq(parameters.agentOs, 'Darwin') }}: + pool: + vmImage: 'macOS-latest' + + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + ${{ if parameters.isBuiltFromVmr }}: + enableSbom: false + ${{ else }}: + enablePublishBuildAssets: true + enablePublishUsingPipelines: true + enableTelemetry: true + helixRepo: dotnet/installer + workspace: + clean: all + +# Test parameters + variables: + - ${{ if eq(parameters.agentOs, 'Windows_NT') }}: + - _PackArg: '-pack' + - ${{ if parameters.runTests }}: + - _TestArg: '-test' + - ${{ else }}: + - _TestArg: '' + - ${{ if ne(parameters.agentOs, 'Windows_NT') }}: + - _PackArg: '--pack' + - ${{ if parameters.runTests }}: + - _TestArg: '--test' + - ${{ else }}: + - _TestArg: '' + + - ${{ if parameters.pgoInstrument }}: + - _PgoInstrument: '/p:PgoInstrument=true' + - _PackArg: '' + - ${{ else }}: + - _PgoInstrument: '' + + - ${{ if parameters.linuxPortable }}: + - _LinuxPortable: '--linux-portable' + - ${{ else }}: + - _LinuxPortable: '' + + - ${{ if ne(parameters.runtimeIdentifier, '') }}: + - _RuntimeIdentifier: '--runtime-id ${{ parameters.runtimeIdentifier }}' + - ${{ else }}: + - _RuntimeIdentifier: '' + + - _AgentOSName: ${{ parameters.agentOs }} + - _TeamName: Roslyn-Project-System + - _SignType: test + - _BuildArgs: '/p:DotNetSignType=$(_SignType) $(_PgoInstrument)' + + - ${{ if parameters.isBuiltFromVmr }}: + - installerRoot: '$(Build.SourcesDirectory)/src/installer' + - _SignType: test + - _PushToVSFeed: false + - _BuildArgs: /p:OfficialBuildId=$(BUILD.BUILDNUMBER) + /p:TeamName=$(_TeamName) + /p:DotNetPublishUsingPipelines=true + /p:PublishToSymbolServer=false + $(_PgoInstrument) + - ${{ else }}: + - installerRoot: '$(Build.SourcesDirectory)' + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: DotNet-HelixApi-Access + - _PushToVSFeed: true + - _SignType: real + - _BuildArgs: /p:OfficialBuildId=$(BUILD.BUILDNUMBER) + /p:DotNetSignType=$(_SignType) + /p:TeamName=$(_TeamName) + /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) + $(_PgoInstrument) + + - template: /eng/common/templates/variables/pool-providers.yml + + steps: + - checkout: self + clean: true + - ${{ if eq(parameters.agentOs, 'Windows_NT') }}: + - ${{ if and(not(parameters.isBuiltFromVmr), ne(variables['System.TeamProject'], 'public')) }}: + - task: PowerShell@2 + displayName: Setup Private Feeds Credentials + inputs: + filePath: $(installerRoot)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(installerRoot)/NuGet.config -Password $Env:Token + env: + Token: $(dn-bot-dnceng-artifact-feeds-rw) + - script: $(installerRoot)/build.cmd + $(_TestArg) $(_PackArg) + -publish -ci -sign + -Configuration ${{ parameters.buildConfiguration }} + -Architecture ${{ parameters.buildArchitecture }} + $(_BuildArgs) + ${{ parameters.additionalBuildParameters }} + $(_InternalRuntimeDownloadArgs) + displayName: Build + env: + DOTNET_CLI_UI_LANGUAGE: ${{ parameters.dotnetCLIUILanguage }} + + - ${{ if ne(parameters.agentOs, 'Windows_NT') }}: + - ${{ if and(not(parameters.isBuiltFromVmr), ne(variables['System.TeamProject'], 'public')) }}: + - task: Bash@3 + displayName: Setup Private Feeds Credentials + inputs: + filePath: $(installerRoot)/eng/common/SetupNugetSources.sh + arguments: $(installerRoot)/NuGet.config $Token + env: + Token: $(dn-bot-dnceng-artifact-feeds-rw) + - ${{ if eq(parameters.agentOs, 'Linux') }}: + - script: $(installerRoot)/build.sh + $(_TestArg) $(_PackArg) + --publish --ci + --noprettyprint + --configuration ${{ parameters.buildConfiguration }} + --architecture ${{ parameters.buildArchitecture }} + $(_LinuxPortable) + $(_RuntimeIdentifier) + $(_BuildArgs) + ${{ parameters.additionalBuildParameters }} + $(_InternalRuntimeDownloadArgs) + displayName: Build + + - ${{ if or(eq(parameters.agentOs, 'Darwin'), eq(parameters.agentOs, 'FreeBSD')) }}: + - script: $(installerRoot)/build.sh + $(_TestArg) + --pack --publish --ci + --noprettyprint + --configuration ${{ parameters.buildConfiguration }} + --architecture ${{ parameters.buildArchitecture }} + $(_RuntimeIdentifier) + $(_BuildArgs) + ${{ parameters.additionalBuildParameters }} + $(_InternalRuntimeDownloadArgs) + displayName: Build + + - task: PublishTestResults@2 + displayName: Publish Test Results + inputs: + testRunner: XUnit + testResultsFiles: 'artifacts/TestResults/${{ parameters.buildConfiguration }}/*.xml' + testRunTitle: '$(_AgentOSName)_$(Agent.JobName)' + platform: '$(BuildPlatform)' + configuration: '${{ parameters.buildConfiguration }}' + condition: ne(variables['_TestArg'], '') + + - task: CopyFiles@2 + displayName: Gather Logs + inputs: + SourceFolder: '$(installerRoot)/artifacts' + Contents: | + log/${{ parameters.buildConfiguration }}/**/* + TestResults/${{ parameters.buildConfiguration }}/**/* + TargetFolder: '$(Build.ArtifactStagingDirectory)' + continueOnError: true + condition: always() + + - task: PublishBuildArtifacts@1 + displayName: Publish Logs to VSTS + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: '$(_AgentOSName)_$(Agent.JobName)_$(Build.BuildNumber)' + publishLocation: Container + continueOnError: true + condition: always() diff --git a/eng/build.yml b/eng/build.yml new file mode 100644 index 000000000..3d2a6869c --- /dev/null +++ b/eng/build.yml @@ -0,0 +1,267 @@ +parameters: +# Agent OS identifier and used as job name +- name: agentOs + type: string + +# Job name +- name: jobName + type: string + +# Container to run the build in, if any +- name: container + type: string + default: '' + +# Job timeout +- name: timeoutInMinutes + type: number + default: 180 + +# Build configuration (Debug, Release) +- name: buildConfiguration + type: string + values: + - Debug + - Release + +# Build architecture +- name: buildArchitecture + type: string + values: + - arm + - arm64 + - x64 + - x86 + +# Linux portable. If true, passes portable switch to build +- name: linuxPortable + type: boolean + default: false + +# Runtime Identifier +- name: runtimeIdentifier + type: string + default: '' + +# UI lang +- name: dotnetCLIUILanguage + type: string + default: '' + +# Additional parameters +- name: additionalBuildParameters + type: string + default: '' + +# Run tests +- name: runTests + type: boolean + default: true + +# PGO instrumentation jobs +- name: pgoInstrument + type: boolean + default: false + +- name: isBuiltFromVmr + displayName: True when build is running from dotnet/dotnet + type: boolean + default: false + +jobs: +- template: common/templates-official/job/job.yml + parameters: + # Set up the name of the job. + ${{ if parameters.pgoInstrument }}: + name: PGO_${{ parameters.agentOs }}_${{ parameters.jobName }} + ${{ if not(parameters.pgoInstrument) }}: + name: ${{ parameters.agentOs }}_${{ parameters.jobName }} + + # Set up the pool/machine info to be used based on the Agent OS + ${{ if eq(parameters.agentOs, 'Windows_NT') }}: + enableMicrobuild: true + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + image: 1es-windows-2022-open + os: windows + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + ${{ if eq(parameters.agentOs, 'Linux') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + image: 1es-ubuntu-2004-open + os: linux + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + image: 1es-ubuntu-2204 + os: linux + container: ${{ parameters.container }} + ${{ if eq(parameters.agentOs, 'Darwin') }}: + pool: + name: Azure Pipelines + image: macOS-latest + os: macOS + + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + ${{ if parameters.isBuiltFromVmr }}: + enableSbom: false + ${{ else }}: + enablePublishBuildAssets: true + enablePublishUsingPipelines: true + enableTelemetry: true + helixRepo: dotnet/installer + workspace: + clean: all + + variables: + # Test variables + - ${{ if eq(parameters.agentOs, 'Windows_NT') }}: + - _PackArg: '-pack' + - ${{ if parameters.runTests }}: + - _TestArg: '-test' + - ${{ else }}: + - _TestArg: '' + - ${{ if ne(parameters.agentOs, 'Windows_NT') }}: + - _PackArg: '--pack' + - ${{ if parameters.runTests }}: + - _TestArg: '--test' + - ${{ else }}: + - _TestArg: '' + + - ${{ if parameters.pgoInstrument }}: + - _PgoInstrument: '/p:PgoInstrument=true' + - _PackArg: '' + - ${{ else }}: + - _PgoInstrument: '' + + - ${{ if parameters.linuxPortable }}: + - _LinuxPortable: '--linux-portable' + - ${{ else }}: + - _LinuxPortable: '' + + - ${{ if ne(parameters.runtimeIdentifier, '') }}: + - _RuntimeIdentifier: '--runtime-id ${{ parameters.runtimeIdentifier }}' + - ${{ else }}: + - _RuntimeIdentifier: '' + + - _AgentOSName: ${{ parameters.agentOs }} + - _TeamName: Roslyn-Project-System + - _SignType: test + - _BuildArgs: '/p:DotNetSignType=$(_SignType) $(_PgoInstrument)' + + - ${{ if parameters.isBuiltFromVmr }}: + - installerRoot: '$(Build.SourcesDirectory)/src/installer' + - _SignType: test + - _PushToVSFeed: false + - _BuildArgs: /p:OfficialBuildId=$(BUILD.BUILDNUMBER) + /p:TeamName=$(_TeamName) + /p:DotNetPublishUsingPipelines=true + /p:PublishToSymbolServer=false + $(_PgoInstrument) + - ${{ else }}: + - installerRoot: '$(Build.SourcesDirectory)' + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: DotNet-HelixApi-Access + - _PushToVSFeed: true + - _SignType: real + - _BuildArgs: /p:OfficialBuildId=$(BUILD.BUILDNUMBER) + /p:DotNetSignType=$(_SignType) + /p:TeamName=$(_TeamName) + /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) + $(_PgoInstrument) + + - template: /eng/common/templates-official/variables/pool-providers.yml + + steps: + - checkout: self + clean: true + - ${{ if eq(parameters.agentOs, 'Windows_NT') }}: + - ${{ if and(not(parameters.isBuiltFromVmr), ne(variables['System.TeamProject'], 'public')) }}: + - task: PowerShell@2 + displayName: Setup Private Feeds Credentials + inputs: + filePath: $(installerRoot)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(installerRoot)/NuGet.config -Password $Env:Token + env: + Token: $(dn-bot-dnceng-artifact-feeds-rw) + - script: $(installerRoot)/build.cmd + $(_TestArg) $(_PackArg) + -publish -ci -sign + -Configuration ${{ parameters.buildConfiguration }} + -Architecture ${{ parameters.buildArchitecture }} + $(_BuildArgs) + ${{ parameters.additionalBuildParameters }} + $(_InternalRuntimeDownloadArgs) + displayName: Build + env: + DOTNET_CLI_UI_LANGUAGE: ${{ parameters.dotnetCLIUILanguage }} + + - ${{ if ne(parameters.agentOs, 'Windows_NT') }}: + - ${{ if and(not(parameters.isBuiltFromVmr), ne(variables['System.TeamProject'], 'public')) }}: + - task: Bash@3 + displayName: Setup Private Feeds Credentials + inputs: + filePath: $(installerRoot)/eng/common/SetupNugetSources.sh + arguments: $(installerRoot)/NuGet.config $Token + env: + Token: $(dn-bot-dnceng-artifact-feeds-rw) + - ${{ if eq(parameters.agentOs, 'Linux') }}: + - script: $(installerRoot)/build.sh + $(_TestArg) $(_PackArg) + --publish --ci + --noprettyprint + --configuration ${{ parameters.buildConfiguration }} + --architecture ${{ parameters.buildArchitecture }} + $(_LinuxPortable) + $(_RuntimeIdentifier) + $(_BuildArgs) + ${{ parameters.additionalBuildParameters }} + $(_InternalRuntimeDownloadArgs) + displayName: Build + + - ${{ if or(eq(parameters.agentOs, 'Darwin'), eq(parameters.agentOs, 'FreeBSD')) }}: + - script: $(installerRoot)/build.sh + $(_TestArg) + --pack --publish --ci + --noprettyprint + --configuration ${{ parameters.buildConfiguration }} + --architecture ${{ parameters.buildArchitecture }} + $(_RuntimeIdentifier) + $(_BuildArgs) + ${{ parameters.additionalBuildParameters }} + $(_InternalRuntimeDownloadArgs) + displayName: Build + + - task: PublishTestResults@2 + displayName: Publish Test Results + inputs: + testRunner: XUnit + testResultsFiles: 'artifacts/TestResults/${{ parameters.buildConfiguration }}/*.xml' + testRunTitle: '$(_AgentOSName)_$(Agent.JobName)' + platform: '$(BuildPlatform)' + configuration: '${{ parameters.buildConfiguration }}' + condition: ne(variables['_TestArg'], '') + + - task: CopyFiles@2 + displayName: Gather Logs + inputs: + SourceFolder: '$(installerRoot)/artifacts' + Contents: | + log/${{ parameters.buildConfiguration }}/**/* + TestResults/${{ parameters.buildConfiguration }}/**/* + TargetFolder: '$(Build.ArtifactStagingDirectory)' + continueOnError: true + condition: always() + + - task: 1ES.PublishBuildArtifacts@1 + displayName: Publish Logs to VSTS + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: '$(_AgentOSName)_$(Agent.JobName)_$(Build.BuildNumber)' + publishLocation: Container + continueOnError: true + condition: always() diff --git a/eng/common/BuildConfiguration/build-configuration.json b/eng/common/BuildConfiguration/build-configuration.json new file mode 100644 index 000000000..3d1cc8989 --- /dev/null +++ b/eng/common/BuildConfiguration/build-configuration.json @@ -0,0 +1,4 @@ +{ + "RetryCountLimit": 1, + "RetryByAnyError": false +} diff --git a/eng/common/CIBuild.cmd b/eng/common/CIBuild.cmd new file mode 100644 index 000000000..56c2f25ac --- /dev/null +++ b/eng/common/CIBuild.cmd @@ -0,0 +1,2 @@ +@echo off +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" -restore -build -test -sign -pack -publish -ci %*" \ No newline at end of file diff --git a/eng/common/PSScriptAnalyzerSettings.psd1 b/eng/common/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 000000000..4c1ea7c98 --- /dev/null +++ b/eng/common/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,11 @@ +@{ + IncludeRules=@('PSAvoidUsingCmdletAliases', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidUsingInvokeExpression', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUseCmdletCorrectly', + 'PSStandardDSCFunctionsInResource', + 'PSUseIdenticalMandatoryParametersForDSC', + 'PSUseIdenticalParametersForDSC') +} \ No newline at end of file diff --git a/eng/common/README.md b/eng/common/README.md new file mode 100644 index 000000000..ff49c3715 --- /dev/null +++ b/eng/common/README.md @@ -0,0 +1,28 @@ +# Don't touch this folder + + uuuuuuuuuuuuuuuuuuuu + u" uuuuuuuuuuuuuuuuuu "u + u" u$$$$$$$$$$$$$$$$$$$$u "u + u" u$$$$$$$$$$$$$$$$$$$$$$$$u "u + u" u$$$$$$$$$$$$$$$$$$$$$$$$$$$$u "u + u" u$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$u "u + u" u$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$u "u + $ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $ + $ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $ + $ $$$" ... "$... ...$" ... "$$$ ... "$$$ $ + $ $$$u `"$$$$$$$ $$$ $$$$$ $$ $$$ $$$ $ + $ $$$$$$uu "$$$$ $$$ $$$$$ $$ """ u$$$ $ + $ $$$""$$$ $$$$ $$$u "$$$" u$$ $$$$$$$$ $ + $ $$$$....,$$$$$..$$$$$....,$$$$..$$$$$$$$ $ + $ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $ + "u "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" u" + "u "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" u" + "u "$$$$$$$$$$$$$$$$$$$$$$$$$$$$" u" + "u "$$$$$$$$$$$$$$$$$$$$$$$$" u" + "u "$$$$$$$$$$$$$$$$$$$$" u" + "u """""""""""""""""" u" + """""""""""""""""""" + +!!! Changes made in this directory are subject to being overwritten by automation !!! + +The files in this directory are shared by all Arcade repos and managed by automation. If you need to make changes to these files, open an issue or submit a pull request to https://github.com/dotnet/arcade first. diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 new file mode 100644 index 000000000..efa2fd72b --- /dev/null +++ b/eng/common/SetupNugetSources.ps1 @@ -0,0 +1,167 @@ +# This file is a temporary workaround for internal builds to be able to restore from private AzDO feeds. +# This file should be removed as part of this issue: https://github.com/dotnet/arcade/issues/4080 +# +# What the script does is iterate over all package sources in the pointed NuGet.config and add a credential entry +# under for each Maestro managed private feed. Two additional credential +# entries are also added for the two private static internal feeds: dotnet3-internal and dotnet3-internal-transport. +# +# This script needs to be called in every job that will restore packages and which the base repo has +# private AzDO feeds in the NuGet.config. +# +# See example YAML call for this script below. Note the use of the variable `$(dn-bot-dnceng-artifact-feeds-rw)` +# from the AzureDevOps-Artifact-Feeds-Pats variable group. +# +# Any disabledPackageSources entries which start with "darc-int" will be re-enabled as part of this script executing +# +# - task: PowerShell@2 +# displayName: Setup Private Feeds Credentials +# condition: eq(variables['Agent.OS'], 'Windows_NT') +# inputs: +# filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 +# arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token +# env: +# Token: $(dn-bot-dnceng-artifact-feeds-rw) + +[CmdletBinding()] +param ( + [Parameter(Mandatory = $true)][string]$ConfigFile, + [Parameter(Mandatory = $true)][string]$Password +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version 2.0 +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +. $PSScriptRoot\tools.ps1 + +# Add source entry to PackageSources +function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) { + $packageSource = $sources.SelectSingleNode("add[@key='$SourceName']") + + if ($packageSource -eq $null) + { + $packageSource = $doc.CreateElement("add") + $packageSource.SetAttribute("key", $SourceName) + $packageSource.SetAttribute("value", $SourceEndPoint) + $sources.AppendChild($packageSource) | Out-Null + } + else { + Write-Host "Package source $SourceName already present." + } + AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd +} + +# Add a credential node for the specified source +function AddCredential($creds, $source, $username, $pwd) { + # Looks for credential configuration for the given SourceName. Create it if none is found. + $sourceElement = $creds.SelectSingleNode($Source) + if ($sourceElement -eq $null) + { + $sourceElement = $doc.CreateElement($Source) + $creds.AppendChild($sourceElement) | Out-Null + } + + # Add the node to the credential if none is found. + $usernameElement = $sourceElement.SelectSingleNode("add[@key='Username']") + if ($usernameElement -eq $null) + { + $usernameElement = $doc.CreateElement("add") + $usernameElement.SetAttribute("key", "Username") + $sourceElement.AppendChild($usernameElement) | Out-Null + } + $usernameElement.SetAttribute("value", $Username) + + # Add the to the credential if none is found. + # Add it as a clear text because there is no support for encrypted ones in non-windows .Net SDKs. + # -> https://github.com/NuGet/Home/issues/5526 + $passwordElement = $sourceElement.SelectSingleNode("add[@key='ClearTextPassword']") + if ($passwordElement -eq $null) + { + $passwordElement = $doc.CreateElement("add") + $passwordElement.SetAttribute("key", "ClearTextPassword") + $sourceElement.AppendChild($passwordElement) | Out-Null + } + + $passwordElement.SetAttribute("value", $pwd) +} + +function InsertMaestroPrivateFeedCredentials($Sources, $Creds, $Username, $pwd) { + $maestroPrivateSources = $Sources.SelectNodes("add[contains(@key,'darc-int')]") + + Write-Host "Inserting credentials for $($maestroPrivateSources.Count) Maestro's private feeds." + + ForEach ($PackageSource in $maestroPrivateSources) { + Write-Host "`tInserting credential for Maestro's feed:" $PackageSource.Key + AddCredential -Creds $creds -Source $PackageSource.Key -Username $Username -pwd $pwd + } +} + +function EnablePrivatePackageSources($DisabledPackageSources) { + $maestroPrivateSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") + ForEach ($DisabledPackageSource in $maestroPrivateSources) { + Write-Host "`tEnsuring private source '$($DisabledPackageSource.key)' is enabled by deleting it from disabledPackageSource" + # Due to https://github.com/NuGet/Home/issues/10291, we must actually remove the disabled entries + $DisabledPackageSources.RemoveChild($DisabledPackageSource) + } +} + +if (!(Test-Path $ConfigFile -PathType Leaf)) { + Write-PipelineTelemetryError -Category 'Build' -Message "Eng/common/SetupNugetSources.ps1 returned a non-zero exit code. Couldn't find the NuGet config file: $ConfigFile" + ExitWithExitCode 1 +} + +if (!$Password) { + Write-PipelineTelemetryError -Category 'Build' -Message 'Eng/common/SetupNugetSources.ps1 returned a non-zero exit code. Please supply a valid PAT' + ExitWithExitCode 1 +} + +# Load NuGet.config +$doc = New-Object System.Xml.XmlDocument +$filename = (Get-Item $ConfigFile).FullName +$doc.Load($filename) + +# Get reference to or create one if none exist already +$sources = $doc.DocumentElement.SelectSingleNode("packageSources") +if ($sources -eq $null) { + $sources = $doc.CreateElement("packageSources") + $doc.DocumentElement.AppendChild($sources) | Out-Null +} + +# Looks for a node. Create it if none is found. +$creds = $doc.DocumentElement.SelectSingleNode("packageSourceCredentials") +if ($creds -eq $null) { + $creds = $doc.CreateElement("packageSourceCredentials") + $doc.DocumentElement.AppendChild($creds) | Out-Null +} + +# Check for disabledPackageSources; we'll enable any darc-int ones we find there +$disabledSources = $doc.DocumentElement.SelectSingleNode("disabledPackageSources") +if ($disabledSources -ne $null) { + Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node" + EnablePrivatePackageSources -DisabledPackageSources $disabledSources +} + +$userName = "dn-bot" + +# Insert credential nodes for Maestro's private feeds +InsertMaestroPrivateFeedCredentials -Sources $sources -Creds $creds -Username $userName -pwd $Password + +# 3.1 uses a different feed url format so it's handled differently here +$dotnet31Source = $sources.SelectSingleNode("add[@key='dotnet3.1']") +if ($dotnet31Source -ne $null) { + AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password + AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password +} + +$dotnetVersions = @('5','6','7','8') + +foreach ($dotnetVersion in $dotnetVersions) { + $feedPrefix = "dotnet" + $dotnetVersion; + $dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']") + if ($dotnetSource -ne $null) { + AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password + AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password + } +} + +$doc.Save($filename) \ No newline at end of file diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh new file mode 100644 index 000000000..d387c7eac --- /dev/null +++ b/eng/common/SetupNugetSources.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash + +# This file is a temporary workaround for internal builds to be able to restore from private AzDO feeds. +# This file should be removed as part of this issue: https://github.com/dotnet/arcade/issues/4080 +# +# What the script does is iterate over all package sources in the pointed NuGet.config and add a credential entry +# under for each Maestro's managed private feed. Two additional credential +# entries are also added for the two private static internal feeds: dotnet3-internal and dotnet3-internal-transport. +# +# This script needs to be called in every job that will restore packages and which the base repo has +# private AzDO feeds in the NuGet.config. +# +# See example YAML call for this script below. Note the use of the variable `$(dn-bot-dnceng-artifact-feeds-rw)` +# from the AzureDevOps-Artifact-Feeds-Pats variable group. +# +# Any disabledPackageSources entries which start with "darc-int" will be re-enabled as part of this script executing. +# +# - task: Bash@3 +# displayName: Setup Private Feeds Credentials +# inputs: +# filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh +# arguments: $(Build.SourcesDirectory)/NuGet.config $Token +# condition: ne(variables['Agent.OS'], 'Windows_NT') +# env: +# Token: $(dn-bot-dnceng-artifact-feeds-rw) + +ConfigFile=$1 +CredToken=$2 +NL='\n' +TB=' ' + +source="${BASH_SOURCE[0]}" + +# resolve $source until the file is no longer a symlink +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +. "$scriptroot/tools.sh" + +if [ ! -f "$ConfigFile" ]; then + Write-PipelineTelemetryError -Category 'Build' "Error: Eng/common/SetupNugetSources.sh returned a non-zero exit code. Couldn't find the NuGet config file: $ConfigFile" + ExitWithExitCode 1 +fi + +if [ -z "$CredToken" ]; then + Write-PipelineTelemetryError -category 'Build' "Error: Eng/common/SetupNugetSources.sh returned a non-zero exit code. Please supply a valid PAT" + ExitWithExitCode 1 +fi + +if [[ `uname -s` == "Darwin" ]]; then + NL=$'\\\n' + TB='' +fi + +# Ensure there is a ... section. +grep -i "" $ConfigFile +if [ "$?" != "0" ]; then + echo "Adding ... section." + ConfigNodeHeader="" + PackageSourcesTemplate="${TB}${NL}${TB}" + + sed -i.bak "s|$ConfigNodeHeader|$ConfigNodeHeader${NL}$PackageSourcesTemplate|" $ConfigFile +fi + +# Ensure there is a ... section. +grep -i "" $ConfigFile +if [ "$?" != "0" ]; then + echo "Adding ... section." + + PackageSourcesNodeFooter="" + PackageSourceCredentialsTemplate="${TB}${NL}${TB}" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourcesNodeFooter${NL}$PackageSourceCredentialsTemplate|" $ConfigFile +fi + +PackageSources=() + +# Ensure dotnet3.1-internal and dotnet3.1-internal-transport are in the packageSources if the public dotnet3.1 feeds are present +grep -i "" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=('dotnet3.1-internal') + + grep -i "" $ConfigFile + if [ "$?" != "0" ]; then + echo "Adding dotnet3.1-internal-transport to the packageSources." + PackageSourcesNodeFooter="" + PackageSourceTemplate="${TB}" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=('dotnet3.1-internal-transport') +fi + +DotNetVersions=('5' '6' '7' '8') + +for DotNetVersion in ${DotNetVersions[@]} ; do + FeedPrefix="dotnet${DotNetVersion}"; + grep -i "" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=("$FeedPrefix-internal") + + grep -i "" $ConfigFile + if [ "$?" != "0" ]; then + echo "Adding $FeedPrefix-internal-transport to the packageSources." + PackageSourcesNodeFooter="" + PackageSourceTemplate="${TB}" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=("$FeedPrefix-internal-transport") + fi +done + +# I want things split line by line +PrevIFS=$IFS +IFS=$'\n' +PackageSources+="$IFS" +PackageSources+=$(grep -oh '"darc-int-[^"]*"' $ConfigFile | tr -d '"') +IFS=$PrevIFS + +for FeedName in ${PackageSources[@]} ; do + # Check if there is no existing credential for this FeedName + grep -i "<$FeedName>" $ConfigFile + if [ "$?" != "0" ]; then + echo "Adding credentials for $FeedName." + + PackageSourceCredentialsNodeFooter="" + NewCredential="${TB}${TB}<$FeedName>${NL}${NL}${NL}" + + sed -i.bak "s|$PackageSourceCredentialsNodeFooter|$NewCredential${NL}$PackageSourceCredentialsNodeFooter|" $ConfigFile + fi +done + +# Re-enable any entries in disabledPackageSources where the feed name contains darc-int +grep -i "" $ConfigFile +if [ "$?" == "0" ]; then + DisabledDarcIntSources=() + echo "Re-enabling any disabled \"darc-int\" package sources in $ConfigFile" + DisabledDarcIntSources+=$(grep -oh '"darc-int-[^"]*" value="true"' $ConfigFile | tr -d '"') + for DisabledSourceName in ${DisabledDarcIntSources[@]} ; do + if [[ $DisabledSourceName == darc-int* ]] + then + OldDisableValue="" + NewDisableValue="" + sed -i.bak "s|$OldDisableValue|$NewDisableValue|" $ConfigFile + echo "Neutralized disablePackageSources entry for '$DisabledSourceName'" + fi + done +fi diff --git a/eng/common/build.cmd b/eng/common/build.cmd new file mode 100644 index 000000000..99daf368a --- /dev/null +++ b/eng/common/build.cmd @@ -0,0 +1,3 @@ +@echo off +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0build.ps1""" %*" +exit /b %ErrorLevel% diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 new file mode 100644 index 000000000..438f9920c --- /dev/null +++ b/eng/common/build.ps1 @@ -0,0 +1,169 @@ +[CmdletBinding(PositionalBinding=$false)] +Param( + [string][Alias('c')]$configuration = "Debug", + [string]$platform = $null, + [string] $projects, + [string][Alias('v')]$verbosity = "minimal", + [string] $msbuildEngine = $null, + [bool] $warnAsError = $true, + [bool] $nodeReuse = $true, + [switch][Alias('r')]$restore, + [switch] $deployDeps, + [switch][Alias('b')]$build, + [switch] $rebuild, + [switch] $deploy, + [switch][Alias('t')]$test, + [switch] $integrationTest, + [switch] $performanceTest, + [switch] $sign, + [switch] $pack, + [switch] $publish, + [switch] $clean, + [switch][Alias('pb')]$productBuild, + [switch][Alias('bl')]$binaryLog, + [switch][Alias('nobl')]$excludeCIBinarylog, + [switch] $ci, + [switch] $prepareMachine, + [string] $runtimeSourceFeed = '', + [string] $runtimeSourceFeedKey = '', + [switch] $excludePrereleaseVS, + [switch] $nativeToolsOnMachine, + [switch] $help, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties +) + +# Unset 'Platform' environment variable to avoid unwanted collision in InstallDotNetCore.targets file +# some computer has this env var defined (e.g. Some HP) +if($env:Platform) { + $env:Platform="" +} +function Print-Usage() { + Write-Host "Common settings:" + Write-Host " -configuration Build configuration: 'Debug' or 'Release' (short: -c)" + Write-Host " -platform Platform configuration: 'x86', 'x64' or any valid Platform value to pass to msbuild" + Write-Host " -verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic] (short: -v)" + Write-Host " -binaryLog Output binary log (short: -bl)" + Write-Host " -help Print help and exit" + Write-Host "" + + Write-Host "Actions:" + Write-Host " -restore Restore dependencies (short: -r)" + Write-Host " -build Build solution (short: -b)" + Write-Host " -rebuild Rebuild solution" + Write-Host " -deploy Deploy built VSIXes" + Write-Host " -deployDeps Deploy dependencies (e.g. VSIXes for integration tests)" + Write-Host " -test Run all unit tests in the solution (short: -t)" + Write-Host " -integrationTest Run all integration tests in the solution" + Write-Host " -performanceTest Run all performance tests in the solution" + Write-Host " -pack Package build outputs into NuGet packages and Willow components" + Write-Host " -sign Sign build outputs" + Write-Host " -publish Publish artifacts (e.g. symbols)" + Write-Host " -clean Clean the solution" + Write-Host " -productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" + Write-Host "" + + Write-Host "Advanced settings:" + Write-Host " -projects Semi-colon delimited list of sln/proj's to build. Globbing is supported (*.sln)" + Write-Host " -ci Set when running on CI server" + Write-Host " -excludeCIBinarylog Don't output binary log (short: -nobl)" + Write-Host " -prepareMachine Prepare machine for CI run, clean up processes after build" + Write-Host " -warnAsError Sets warnaserror msbuild parameter ('true' or 'false')" + Write-Host " -msbuildEngine Msbuild engine to use to run build ('dotnet', 'vs', or unspecified)." + Write-Host " -excludePrereleaseVS Set to exclude build engines in prerelease versions of Visual Studio" + Write-Host " -nativeToolsOnMachine Sets the native tools on machine environment variable (indicating that the script should use native tools on machine)" + Write-Host "" + + Write-Host "Command line arguments not listed above are passed thru to msbuild." + Write-Host "The above arguments can be shortened as much as to be unambiguous (e.g. -co for configuration, -t for test, etc.)." +} + +. $PSScriptRoot\tools.ps1 + +function InitializeCustomToolset { + if (-not $restore) { + return + } + + $script = Join-Path $EngRoot 'restore-toolset.ps1' + + if (Test-Path $script) { + . $script + } +} + +function Build { + $toolsetBuildProj = InitializeToolset + InitializeCustomToolset + + $bl = if ($binaryLog) { '/bl:' + (Join-Path $LogDir 'Build.binlog') } else { '' } + $platformArg = if ($platform) { "/p:Platform=$platform" } else { '' } + + if ($projects) { + # Re-assign properties to a new variable because PowerShell doesn't let us append properties directly for unclear reasons. + # Explicitly set the type as string[] because otherwise PowerShell would make this char[] if $properties is empty. + [string[]] $msbuildArgs = $properties + + # Resolve relative project paths into full paths + $projects = ($projects.Split(';').ForEach({Resolve-Path $_}) -join ';') + + $msbuildArgs += "/p:Projects=$projects" + $properties = $msbuildArgs + } + + MSBuild $toolsetBuildProj ` + $bl ` + $platformArg ` + /p:Configuration=$configuration ` + /p:RepoRoot=$RepoRoot ` + /p:Restore=$restore ` + /p:DeployDeps=$deployDeps ` + /p:Build=$build ` + /p:Rebuild=$rebuild ` + /p:Deploy=$deploy ` + /p:Test=$test ` + /p:Pack=$pack ` + /p:DotNetBuildRepo=$productBuild ` + /p:IntegrationTest=$integrationTest ` + /p:PerformanceTest=$performanceTest ` + /p:Sign=$sign ` + /p:Publish=$publish ` + @properties +} + +try { + if ($clean) { + if (Test-Path $ArtifactsDir) { + Remove-Item -Recurse -Force $ArtifactsDir + Write-Host 'Artifacts directory deleted.' + } + exit 0 + } + + if ($help -or (($null -ne $properties) -and ($properties.Contains('/help') -or $properties.Contains('/?')))) { + Print-Usage + exit 0 + } + + if ($ci) { + if (-not $excludeCIBinarylog) { + $binaryLog = $true + } + $nodeReuse = $false + } + + if ($nativeToolsOnMachine) { + $env:NativeToolsOnMachine = $true + } + if ($restore) { + InitializeNativeTools + } + + Build +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ + ExitWithExitCode 1 +} + +ExitWithExitCode 0 diff --git a/eng/common/build.sh b/eng/common/build.sh new file mode 100755 index 000000000..ac1ee8620 --- /dev/null +++ b/eng/common/build.sh @@ -0,0 +1,260 @@ +#!/usr/bin/env bash + +# Stop script if unbound variable found (use ${var:-} if intentional) +set -u + +# Stop script if command returns non-zero exit code. +# Prevents hidden errors caused by missing error code propagation. +set -e + +usage() +{ + echo "Common settings:" + echo " --configuration Build configuration: 'Debug' or 'Release' (short: -c)" + echo " --verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic] (short: -v)" + echo " --binaryLog Create MSBuild binary log (short: -bl)" + echo " --help Print help and exit (short: -h)" + echo "" + + echo "Actions:" + echo " --restore Restore dependencies (short: -r)" + echo " --build Build solution (short: -b)" + echo " --sourceBuild Source-build the solution (short: -sb)" + echo " Will additionally trigger the following actions: --restore, --build, --pack" + echo " If --configuration is not set explicitly, will also set it to 'Release'" + echo " --productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" + echo " Will additionally trigger the following actions: --restore, --build, --pack" + echo " If --configuration is not set explicitly, will also set it to 'Release'" + echo " --rebuild Rebuild solution" + echo " --test Run all unit tests in the solution (short: -t)" + echo " --integrationTest Run all integration tests in the solution" + echo " --performanceTest Run all performance tests in the solution" + echo " --pack Package build outputs into NuGet packages and Willow components" + echo " --sign Sign build outputs" + echo " --publish Publish artifacts (e.g. symbols)" + echo " --clean Clean the solution" + echo "" + + echo "Advanced settings:" + echo " --projects Project or solution file(s) to build" + echo " --ci Set when running on CI server" + echo " --excludeCIBinarylog Don't output binary log (short: -nobl)" + echo " --prepareMachine Prepare machine for CI run, clean up processes after build" + echo " --nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')" + echo " --warnAsError Sets warnaserror msbuild parameter ('true' or 'false')" + echo "" + echo "Command line arguments not listed above are passed thru to msbuild." + echo "Arguments can also be passed in with a single hyphen." +} + +source="${BASH_SOURCE[0]}" + +# resolve $source until the file is no longer a symlink +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +restore=false +build=false +source_build=false +product_build=false +rebuild=false +test=false +integration_test=false +performance_test=false +pack=false +publish=false +sign=false +public=false +ci=false +clean=false + +warn_as_error=true +node_reuse=true +binary_log=false +exclude_ci_binary_log=false +pipelines_log=false + +projects='' +configuration='' +prepare_machine=false +verbosity='minimal' +runtime_source_feed='' +runtime_source_feed_key='' + +properties='' +while [[ $# > 0 ]]; do + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -help|-h) + usage + exit 0 + ;; + -clean) + clean=true + ;; + -configuration|-c) + configuration=$2 + shift + ;; + -verbosity|-v) + verbosity=$2 + shift + ;; + -binarylog|-bl) + binary_log=true + ;; + -excludecibinarylog|-nobl) + exclude_ci_binary_log=true + ;; + -pipelineslog|-pl) + pipelines_log=true + ;; + -restore|-r) + restore=true + ;; + -build|-b) + build=true + ;; + -rebuild) + rebuild=true + ;; + -pack) + pack=true + ;; + -sourcebuild|-sb) + build=true + source_build=true + product_build=true + restore=true + pack=true + ;; + -productBuild|-pb) + build=true + product_build=true + restore=true + pack=true + ;; + -test|-t) + test=true + ;; + -integrationtest) + integration_test=true + ;; + -performancetest) + performance_test=true + ;; + -sign) + sign=true + ;; + -publish) + publish=true + ;; + -preparemachine) + prepare_machine=true + ;; + -projects) + projects=$2 + shift + ;; + -ci) + ci=true + ;; + -warnaserror) + warn_as_error=$2 + shift + ;; + -nodereuse) + node_reuse=$2 + shift + ;; + -runtimesourcefeed) + runtime_source_feed=$2 + shift + ;; + -runtimesourcefeedkey) + runtime_source_feed_key=$2 + shift + ;; + *) + properties="$properties $1" + ;; + esac + + shift +done + +if [[ -z "$configuration" ]]; then + if [[ "$source_build" = true ]]; then configuration="Release"; else configuration="Debug"; fi +fi + +if [[ "$ci" == true ]]; then + pipelines_log=true + node_reuse=false + if [[ "$exclude_ci_binary_log" == false ]]; then + binary_log=true + fi +fi + +. "$scriptroot/tools.sh" + +function InitializeCustomToolset { + local script="$eng_root/restore-toolset.sh" + + if [[ -a "$script" ]]; then + . "$script" + fi +} + +function Build { + InitializeToolset + InitializeCustomToolset + + if [[ ! -z "$projects" ]]; then + properties="$properties /p:Projects=$projects" + fi + + local bl="" + if [[ "$binary_log" == true ]]; then + bl="/bl:\"$log_dir/Build.binlog\"" + fi + + MSBuild $_InitializeToolset \ + $bl \ + /p:Configuration=$configuration \ + /p:RepoRoot="$repo_root" \ + /p:Restore=$restore \ + /p:Build=$build \ + /p:DotNetBuildRepo=$product_build \ + /p:ArcadeBuildFromSource=$source_build \ + /p:DotNetBuildSourceOnly=$source_build \ + /p:Rebuild=$rebuild \ + /p:Test=$test \ + /p:Pack=$pack \ + /p:IntegrationTest=$integration_test \ + /p:PerformanceTest=$performance_test \ + /p:Sign=$sign \ + /p:Publish=$publish \ + $properties + + ExitWithExitCode 0 +} + +if [[ "$clean" == true ]]; then + if [ -d "$artifacts_dir" ]; then + rm -rf $artifacts_dir + echo "Artifacts directory deleted." + fi + exit 0 +fi + +if [[ "$restore" == true ]]; then + InitializeNativeTools +fi + +Build diff --git a/eng/common/cibuild.sh b/eng/common/cibuild.sh new file mode 100755 index 000000000..1a02c0dec --- /dev/null +++ b/eng/common/cibuild.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" + +# resolve $SOURCE until the file is no longer a symlink +while [[ -h $source ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + + # if $source was a relative symlink, we need to resolve it relative to the path where + # the symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +. "$scriptroot/build.sh" --restore --build --test --pack --publish --ci $@ \ No newline at end of file diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml new file mode 100644 index 000000000..dc3bd560a --- /dev/null +++ b/eng/common/core-templates/job/job.yml @@ -0,0 +1,266 @@ +parameters: +# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + cancelTimeoutInMinutes: '' + condition: '' + container: '' + continueOnError: false + dependsOn: '' + displayName: '' + pool: '' + steps: [] + strategy: '' + timeoutInMinutes: '' + variables: [] + workspace: '' + templateContext: {} + +# Job base template specific parameters + # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md + # publishing defaults + artifacts: '' + enableMicrobuild: false + enablePublishBuildArtifacts: false + enablePublishBuildAssets: false + enablePublishTestResults: false + enablePublishUsingPipelines: false + enableBuildRetry: false + disableComponentGovernance: '' + componentGovernanceIgnoreDirectories: '' + mergeTestResults: false + testRunTitle: '' + testResultsFormat: '' + name: '' + preSteps: [] + artifactPublishSteps: [] + runAsPublic: false + +# Sbom related params + enableSbom: true + PackageVersion: 9.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + +# 1es specific parameters + is1ESPipeline: '' + +jobs: +- job: ${{ parameters.name }} + + ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}: + cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }} + + ${{ if ne(parameters.condition, '') }}: + condition: ${{ parameters.condition }} + + ${{ if ne(parameters.container, '') }}: + container: ${{ parameters.container }} + + ${{ if ne(parameters.continueOnError, '') }}: + continueOnError: ${{ parameters.continueOnError }} + + ${{ if ne(parameters.dependsOn, '') }}: + dependsOn: ${{ parameters.dependsOn }} + + ${{ if ne(parameters.displayName, '') }}: + displayName: ${{ parameters.displayName }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + + ${{ if ne(parameters.strategy, '') }}: + strategy: ${{ parameters.strategy }} + + ${{ if ne(parameters.timeoutInMinutes, '') }}: + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + + ${{ if ne(parameters.templateContext, '') }}: + templateContext: ${{ parameters.templateContext }} + + variables: + - ${{ if ne(parameters.enableTelemetry, 'false') }}: + - name: DOTNET_CLI_TELEMETRY_PROFILE + value: '$(Build.Repository.Uri)' + - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: + - name: EnableRichCodeNavigation + value: 'true' + # Retry signature validation up to three times, waiting 2 seconds between attempts. + # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures + - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY + value: 3,2000 + - ${{ each variable in parameters.variables }}: + # handle name-value variable syntax + # example: + # - name: [key] + # value: [value] + - ${{ if ne(variable.name, '') }}: + - name: ${{ variable.name }} + value: ${{ variable.value }} + + # handle variable groups + - ${{ if ne(variable.group, '') }}: + - group: ${{ variable.group }} + + # handle template variable syntax + # example: + # - template: path/to/template.yml + # parameters: + # [key]: [value] + - ${{ if ne(variable.template, '') }}: + - template: ${{ variable.template }} + ${{ if ne(variable.parameters, '') }}: + parameters: ${{ variable.parameters }} + + # handle key-value variable syntax. + # example: + # - [key]: [value] + - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}: + - ${{ each pair in variable }}: + - name: ${{ pair.key }} + value: ${{ pair.value }} + + # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds + - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: DotNet-HelixApi-Access + + ${{ if ne(parameters.workspace, '') }}: + workspace: ${{ parameters.workspace }} + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if ne(parameters.preSteps, '') }}: + - ${{ each preStep in parameters.preSteps }}: + - ${{ preStep }} + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if eq(parameters.enableMicrobuild, 'true') }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin + inputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + env: + TeamName: $(_TeamName) + MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' + continueOnError: ${{ parameters.continueOnError }} + condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) + + - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: + - task: NuGetAuthenticate@1 + + - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: + - task: DownloadPipelineArtifact@2 + inputs: + buildType: current + artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }} + targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }} + itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }} + + - ${{ each step in parameters.steps }}: + - ${{ step }} + + - ${{ if eq(parameters.enableRichCodeNavigation, true) }}: + - task: RichCodeNavIndexer@0 + displayName: RichCodeNav Upload + inputs: + languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} + environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'internal') }} + richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin + uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }} + continueOnError: true + + - template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ if eq(parameters.disableComponentGovernance, '') }}: + ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: + disableComponentGovernance: false + ${{ else }}: + disableComponentGovernance: true + ${{ else }}: + disableComponentGovernance: ${{ parameters.disableComponentGovernance }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} + + - ${{ if eq(parameters.enableMicrobuild, 'true') }}: + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - task: MicroBuildCleanup@1 + displayName: Execute Microbuild cleanup tasks + condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + env: + TeamName: $(_TeamName) + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: + - template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + PackageVersion: ${{ parameters.packageVersion}} + BuildDropPath: ${{ parameters.buildDropPath }} + IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} + publishArtifacts: false + + # Publish test results + - ${{ if and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')) }}: + - ${{ if eq(parameters.testResultsFormat, 'xunit') }}: + - task: PublishTestResults@2 + displayName: Publish XUnit Test Results + inputs: + testResultsFormat: 'xUnit' + testResultsFiles: '*.xml' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() + - ${{ if eq(parameters.testResultsFormat, 'vstest') }}: + - task: PublishTestResults@2 + displayName: Publish TRX Test Results + inputs: + testResultsFormat: 'VSTest' + testResultsFiles: '*.trx' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() + + # gather artifacts + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - task: CopyFiles@2 + displayName: Gather binaries for publish to artifacts + inputs: + SourceFolder: 'artifacts/bin' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin' + - task: CopyFiles@2 + displayName: Gather packages for publish to artifacts + inputs: + SourceFolder: 'artifacts/packages' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages' + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - task: CopyFiles@2 + displayName: Gather logs for publish to artifacts + inputs: + SourceFolder: 'artifacts/log' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log' + + - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: + - task: CopyFiles@2 + displayName: Gather logs for publish to artifacts + inputs: + SourceFolder: 'artifacts/log/$(_BuildConfig)' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - task: CopyFiles@2 + displayName: Gather buildconfiguration for build retry + inputs: + SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' + + - ${{ each step in parameters.artifactPublishSteps }}: + - ${{ step }} diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml new file mode 100644 index 000000000..00feec8eb --- /dev/null +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -0,0 +1,121 @@ +parameters: + # Optional: dependencies of the job + dependsOn: '' + + # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + pool: '' + + CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex + GithubPat: $(BotAccount-dotnet-bot-repo-PAT) + + SourcesDirectory: $(Build.SourcesDirectory) + CreatePr: true + AutoCompletePr: false + ReusePr: true + UseLfLineEndings: true + UseCheckedInLocProjectJson: false + SkipLocProjectJsonGeneration: false + LanguageSet: VS_Main_Languages + LclSource: lclFilesInRepo + LclPackageId: '' + RepoType: gitHub + GitHubOrg: dotnet + MirrorRepo: '' + MirrorBranch: main + condition: '' + JobNameSuffix: '' + is1ESPipeline: '' +jobs: +- job: OneLocBuild${{ parameters.JobNameSuffix }} + + dependsOn: ${{ parameters.dependsOn }} + + displayName: OneLocBuild${{ parameters.JobNameSuffix }} + + variables: + - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat + - name: _GenerateLocProjectArguments + value: -SourcesDirectory ${{ parameters.SourcesDirectory }} + -LanguageSet "${{ parameters.LanguageSet }}" + -CreateNeutralXlfs + - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: + - name: _GenerateLocProjectArguments + value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + ${{ if eq(parameters.pool, '') }}: + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: + - task: Powershell@2 + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + arguments: $(_GenerateLocProjectArguments) + displayName: Generate LocProject.json + condition: ${{ parameters.condition }} + + - task: OneLocBuild@2 + displayName: OneLocBuild + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + inputs: + locProj: eng/Localize/LocProject.json + outDir: $(Build.ArtifactStagingDirectory) + lclSource: ${{ parameters.LclSource }} + lclPackageId: ${{ parameters.LclPackageId }} + isCreatePrSelected: ${{ parameters.CreatePr }} + isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} + ${{ if eq(parameters.CreatePr, true) }}: + isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + isShouldReusePrSelected: ${{ parameters.ReusePr }} + packageSourceAuth: patAuth + patVariable: ${{ parameters.CeapexPat }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + repoType: ${{ parameters.RepoType }} + gitHubPatVariable: "${{ parameters.GithubPat }}" + ${{ if ne(parameters.MirrorRepo, '') }}: + isMirrorRepoSelected: true + gitHubOrganization: ${{ parameters.GitHubOrg }} + mirrorRepo: ${{ parameters.MirrorRepo }} + mirrorBranch: ${{ parameters.MirrorBranch }} + condition: ${{ parameters.condition }} + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish Localization Files + pathToPublish: '$(Build.ArtifactStagingDirectory)/loc' + publishLocation: Container + artifactName: Loc + condition: ${{ parameters.condition }} + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish LocProject.json + pathToPublish: '$(Build.SourcesDirectory)/eng/Localize/' + publishLocation: Container + artifactName: Loc + condition: ${{ parameters.condition }} \ No newline at end of file diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml new file mode 100644 index 000000000..8fe929954 --- /dev/null +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -0,0 +1,172 @@ +parameters: + configuration: 'Debug' + + # Optional: condition for the job to run + condition: '' + + # Optional: 'true' if future jobs should run even if this job fails + continueOnError: false + + # Optional: dependencies of the job + dependsOn: '' + + # Optional: Include PublishBuildArtifacts task + enablePublishBuildArtifacts: false + + # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + pool: {} + + # Optional: should run as a public build even in the internal project + # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. + runAsPublic: false + + # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing + publishUsingPipelines: false + + # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing + publishAssetsImmediately: false + + artifactsPublishingAdditionalParameters: '' + + signingValidationAdditionalParameters: '' + + is1ESPipeline: '' + +jobs: +- job: Asset_Registry_Publish + + dependsOn: ${{ parameters.dependsOn }} + timeoutInMinutes: 150 + + ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: + displayName: Publish Assets + ${{ else }}: + displayName: Publish to Build Asset Registry + + variables: + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: Publish-Build-Assets + - group: AzureDevOps-Artifact-Feeds-Pats + - name: runCodesignValidationInjection + value: false + # unconditional - needed for logs publishing (redactor tool version) + - template: /eng/common/core-templates/post-build/common-variables.yml + + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: + name: NetCore1ESPool-Publishing-Internal + image: windows.vs2019.amd64 + os: windows + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - checkout: self + fetchDepth: 3 + clean: true + + - task: DownloadBuildArtifacts@0 + displayName: Download artifact + inputs: + artifactName: AssetManifests + downloadPath: '$(Build.StagingDirectory)/Download' + checkDownloadedFiles: true + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + + - task: NuGetAuthenticate@1 + + - task: PowerShell@2 + displayName: Publish Build Assets + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet + /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' + /p:BuildAssetRegistryToken=$(MaestroAccessToken) + /p:MaestroApiEndpoint=https://maestro.dot.net + /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} + /p:OfficialBuildId=$(Build.BuildNumber) + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + + - task: powershell@2 + displayName: Create ReleaseConfigs Artifact + inputs: + targetType: inline + script: | + New-Item -Path "$(Build.StagingDirectory)/ReleaseConfigs" -ItemType Directory -Force + $filePath = "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt" + Add-Content -Path $filePath -Value $(BARBuildId) + Add-Content -Path $filePath -Value "$(DefaultChannels)" + Add-Content -Path $filePath -Value $(IsStableBuild) + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish ReleaseConfigs Artifact + pathToPublish: '$(Build.StagingDirectory)/ReleaseConfigs' + publishLocation: Container + artifactName: ReleaseConfigs + + - task: powershell@2 + displayName: Check if SymbolPublishingExclusionsFile.txt exists + inputs: + targetType: inline + script: | + $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + if(Test-Path -Path $symbolExclusionfile) + { + Write-Host "SymbolExclusionFile exists" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" + } + else{ + Write-Host "Symbols Exclusion file does not exist" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" + } + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish SymbolPublishingExclusionsFile Artifact + condition: eq(variables['SymbolExclusionFile'], 'true') + pathToPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' + publishLocation: Container + artifactName: ReleaseConfigs + + - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: PowerShell@2 + displayName: Publish Using Darc + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) + -PublishingInfraVersion 3 + -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' + -MaestroToken '$(MaestroApiAccessToken)' + -WaitPublishingFinish true + -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' + -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' + + - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + JobLabel: 'Publish_Artifacts_Logs' diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml new file mode 100644 index 000000000..c0ce4b3c8 --- /dev/null +++ b/eng/common/core-templates/job/source-build.yml @@ -0,0 +1,80 @@ +parameters: + # This template adds arcade-powered source-build to CI. The template produces a server job with a + # default ID 'Source_Build_Complete' to put in a dependency list if necessary. + + # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed. + jobNamePrefix: 'Source_Build' + + # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for + # managed-only repositories. This is an object with these properties: + # + # name: '' + # The name of the job. This is included in the job ID. + # targetRID: '' + # The name of the target RID to use, instead of the one auto-detected by Arcade. + # nonPortable: false + # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than + # linux-x64), and compiling against distro-provided packages rather than portable ones. + # skipPublishValidation: false + # Disables publishing validation. By default, a check is performed to ensure no packages are + # published by source-build. + # container: '' + # A container to use. Runs in docker. + # pool: {} + # A pool to use. Runs directly on an agent. + # buildScript: '' + # Specifies the build script to invoke to perform the build in the repo. The default + # './build.sh' should work for typical Arcade repositories, but this is customizable for + # difficult situations. + # jobProperties: {} + # A list of job properties to inject at the top level, for potential extensibility beyond + # container and pool. + platform: {} + + is1ESPipeline: '' + +jobs: +- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} + displayName: Source-Build (${{ parameters.platform.name }}) + + ${{ each property in parameters.platform.jobProperties }}: + ${{ property.key }}: ${{ property.value }} + + ${{ if ne(parameters.platform.container, '') }}: + container: ${{ parameters.platform.container }} + + ${{ if eq(parameters.platform.pool, '') }}: + # The default VM host AzDO pool. This should be capable of running Docker containers: almost all + # source-build builds run in Docker, including the default managed platform. + # /eng/common/core-templates/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic + ${{ if eq(parameters.is1ESPipeline, 'true') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] + demands: ImageOverride -equals build.ubuntu.2004.amd64 + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] + image: 1es-mariner-2 + os: linux + ${{ else }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64 + ${{ if ne(parameters.platform.pool, '') }}: + pool: ${{ parameters.platform.pool }} + + workspace: + clean: all + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + platform: ${{ parameters.platform }} diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml new file mode 100644 index 000000000..f1938eec1 --- /dev/null +++ b/eng/common/core-templates/job/source-index-stage1.yml @@ -0,0 +1,91 @@ +parameters: + runAsPublic: false + sourceIndexUploadPackageVersion: 2.0.0-20240502.12 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20240129.2 + sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json + sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" + preSteps: [] + binlogPath: artifacts/log/Debug/Build.binlog + condition: '' + dependsOn: '' + pool: '' + is1ESPipeline: '' + +jobs: +- job: SourceIndexStage1 + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} + variables: + - name: SourceIndexUploadPackageVersion + value: ${{ parameters.sourceIndexUploadPackageVersion }} + - name: SourceIndexProcessBinlogPackageVersion + value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }} + - name: SourceIndexPackageSource + value: ${{ parameters.sourceIndexPackageSource }} + - name: BinlogPath + value: ${{ parameters.binlogPath }} + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + ${{ if eq(parameters.pool, '') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + image: windows.vs2022.amd64.open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + image: windows.vs2022.amd64 + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ each preStep in parameters.preSteps }}: + - ${{ preStep }} + + - task: UseDotNet@2 + displayName: Use .NET 8 SDK + inputs: + packageType: sdk + version: 8.0.x + installationPath: $(Agent.TempDirectory)/dotnet + workingDirectory: $(Agent.TempDirectory) + + - script: | + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + displayName: Download Tools + # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. + workingDirectory: $(Agent.TempDirectory) + + - script: ${{ parameters.sourceIndexBuildCommand }} + displayName: Build Repository + + - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output + displayName: Process Binlog into indexable sln + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - task: AzureCLI@2 + displayName: Get stage 1 auth token + inputs: + azureSubscription: 'SourceDotNet Stage1 Publish' + addSpnToEnvironment: true + scriptType: 'ps' + scriptLocation: 'inlineScript' + inlineScript: | + echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId" + echo "##vso[task.setvariable variable=ARM_ID_TOKEN]$env:idToken" + echo "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId" + + - script: | + echo "Client ID: $(ARM_CLIENT_ID)" + echo "ID Token: $(ARM_ID_TOKEN)" + echo "Tenant ID: $(ARM_TENANT_ID)" + az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN) + displayName: "Login to Azure" + + - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1 + displayName: Upload stage1 artifacts to source index \ No newline at end of file diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml new file mode 100644 index 000000000..f2144252c --- /dev/null +++ b/eng/common/core-templates/jobs/codeql-build.yml @@ -0,0 +1,33 @@ +parameters: + # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md + continueOnError: false + # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + jobs: [] + # Optional: if specified, restore and use this version of Guardian instead of the default. + overrideGuardianVersion: '' + is1ESPipeline: '' + +jobs: +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + enableMicrobuild: false + enablePublishBuildArtifacts: false + enablePublishTestResults: false + enablePublishBuildAssets: false + enablePublishUsingPipelines: false + enableTelemetry: true + + variables: + - group: Publish-Build-Assets + # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in + # sync with the packages.config file. + - name: DefaultGuardianVersion + value: 0.109.0 + - name: GuardianPackagesConfigFile + value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + - name: GuardianVersion + value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} + + jobs: ${{ parameters.jobs }} + diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml new file mode 100644 index 000000000..ea69be434 --- /dev/null +++ b/eng/common/core-templates/jobs/jobs.yml @@ -0,0 +1,119 @@ +parameters: + # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md + continueOnError: false + + # Optional: Include PublishBuildArtifacts task + enablePublishBuildArtifacts: false + + # Optional: Enable publishing using release pipelines + enablePublishUsingPipelines: false + + # Optional: Enable running the source-build jobs to build repo from source + enableSourceBuild: false + + # Optional: Parameters for source-build template. + # See /eng/common/core-templates/jobs/source-build.yml for options + sourceBuildParameters: [] + + graphFileGeneration: + # Optional: Enable generating the graph files at the end of the build + enabled: false + # Optional: Include toolset dependencies in the generated graph files + includeToolset: false + + # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + jobs: [] + + # Optional: Override automatically derived dependsOn value for "publish build assets" job + publishBuildAssetsDependsOn: '' + + # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage. + publishAssetsImmediately: false + + # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml) + artifactsPublishingAdditionalParameters: '' + signingValidationAdditionalParameters: '' + + # Optional: should run as a public build even in the internal project + # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. + runAsPublic: false + + enableSourceIndex: false + sourceIndexParams: {} + + artifacts: {} + is1ESPipeline: '' + +# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, +# and some (Microbuild) should only be applied to non-PR cases for internal builds. + +jobs: +- ${{ each job in parameters.jobs }}: + - ${{ if eq(parameters.is1ESPipeline, 'true') }}: + - template: /eng/common/templates-official/job/job.yml + parameters: + # pass along parameters + ${{ each parameter in parameters }}: + ${{ if ne(parameter.key, 'jobs') }}: + ${{ parameter.key }}: ${{ parameter.value }} + + # pass along job properties + ${{ each property in job }}: + ${{ if ne(property.key, 'job') }}: + ${{ property.key }}: ${{ property.value }} + + name: ${{ job.job }} + + - ${{ else }}: + - template: /eng/common/templates/job/job.yml + parameters: + # pass along parameters + ${{ each parameter in parameters }}: + ${{ if ne(parameter.key, 'jobs') }}: + ${{ parameter.key }}: ${{ parameter.value }} + + # pass along job properties + ${{ each property in job }}: + ${{ if ne(property.key, 'job') }}: + ${{ property.key }}: ${{ property.value }} + + name: ${{ job.job }} + +- ${{ if eq(parameters.enableSourceBuild, true) }}: + - template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + allCompletedJobId: Source_Build_Complete + ${{ each parameter in parameters.sourceBuildParameters }}: + ${{ parameter.key }}: ${{ parameter.value }} + +- ${{ if eq(parameters.enableSourceIndex, 'true') }}: + - template: ../job/source-index-stage1.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + runAsPublic: ${{ parameters.runAsPublic }} + ${{ each parameter in parameters.sourceIndexParams }}: + ${{ parameter.key }}: ${{ parameter.value }} + +- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: + - template: ../job/publish-build-assets.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + continueOnError: ${{ parameters.continueOnError }} + dependsOn: + - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: + - ${{ each job in parameters.publishBuildAssetsDependsOn }}: + - ${{ job.job }} + - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: + - ${{ each job in parameters.jobs }}: + - ${{ job.job }} + - ${{ if eq(parameters.enableSourceBuild, true) }}: + - Source_Build_Complete + + runAsPublic: ${{ parameters.runAsPublic }} + publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} + publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }} + enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} + artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} + signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} diff --git a/eng/common/core-templates/jobs/source-build.yml b/eng/common/core-templates/jobs/source-build.yml new file mode 100644 index 000000000..d8e5d0085 --- /dev/null +++ b/eng/common/core-templates/jobs/source-build.yml @@ -0,0 +1,50 @@ +parameters: + # This template adds arcade-powered source-build to CI. A job is created for each platform, as + # well as an optional server job that completes when all platform jobs complete. + + # The name of the "join" job for all source-build platforms. If set to empty string, the job is + # not included. Existing repo pipelines can use this job depend on all source-build jobs + # completing without maintaining a separate list of every single job ID: just depend on this one + # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. + allCompletedJobId: '' + + # See /eng/common/core-templates/job/source-build.yml + jobNamePrefix: 'Source_Build' + + # This is the default platform provided by Arcade, intended for use by a managed-only repo. + defaultManagedPlatform: + name: 'Managed' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' + + # Defines the platforms on which to run build jobs. One job is created for each platform, and the + # object in this array is sent to the job template as 'platform'. If no platforms are specified, + # one job runs on 'defaultManagedPlatform'. + platforms: [] + + is1ESPipeline: '' + +jobs: + +- ${{ if ne(parameters.allCompletedJobId, '') }}: + - job: ${{ parameters.allCompletedJobId }} + displayName: Source-Build Complete + pool: server + dependsOn: + - ${{ each platform in parameters.platforms }}: + - ${{ parameters.jobNamePrefix }}_${{ platform.name }} + - ${{ if eq(length(parameters.platforms), 0) }}: + - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} + +- ${{ each platform in parameters.platforms }}: + - template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobNamePrefix: ${{ parameters.jobNamePrefix }} + platform: ${{ platform }} + +- ${{ if eq(length(parameters.platforms), 0) }}: + - template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobNamePrefix: ${{ parameters.jobNamePrefix }} + platform: ${{ parameters.defaultManagedPlatform }} diff --git a/eng/common/core-templates/post-build/common-variables.yml b/eng/common/core-templates/post-build/common-variables.yml new file mode 100644 index 000000000..b9ede10bf --- /dev/null +++ b/eng/common/core-templates/post-build/common-variables.yml @@ -0,0 +1,24 @@ +variables: + - group: Publish-Build-Assets + + # Whether the build is internal or not + - name: IsInternalBuild + value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} + + # Default Maestro++ API Endpoint and API Version + - name: MaestroApiEndPoint + value: "https://maestro.dot.net" + - name: MaestroApiAccessToken + value: $(MaestroAccessToken) + - name: MaestroApiVersion + value: "2020-02-20" + + - name: SourceLinkCLIVersion + value: 3.0.0 + - name: SymbolToolVersion + value: 1.0.1 + - name: BinlogToolVersion + value: 1.0.11 + + - name: runCodesignValidationInjection + value: false diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml new file mode 100644 index 000000000..865bc1ecb --- /dev/null +++ b/eng/common/core-templates/post-build/post-build.yml @@ -0,0 +1,314 @@ +parameters: + # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. + # Publishing V1 is no longer supported + # Publishing V2 is no longer supported + # Publishing V3 is the default + - name: publishingInfraVersion + displayName: Which version of publishing should be used to promote the build definition? + type: number + default: 3 + values: + - 3 + + - name: BARBuildId + displayName: BAR Build Id + type: number + default: 0 + + - name: PromoteToChannelIds + displayName: Channel to promote BARBuildId to + type: string + default: '' + + - name: enableSourceLinkValidation + displayName: Enable SourceLink validation + type: boolean + default: false + + - name: enableSigningValidation + displayName: Enable signing validation + type: boolean + default: true + + - name: enableSymbolValidation + displayName: Enable symbol validation + type: boolean + default: false + + - name: enableNugetValidation + displayName: Enable NuGet validation + type: boolean + default: true + + - name: publishInstallersAndChecksums + displayName: Publish installers and checksums + type: boolean + default: true + + - name: SDLValidationParameters + type: object + default: + enable: false + publishGdn: false + continueOnError: false + params: '' + artifactNames: '' + downloadArtifacts: true + + # These parameters let the user customize the call to sdk-task.ps1 for publishing + # symbols & general artifacts as well as for signing validation + - name: symbolPublishingAdditionalParameters + displayName: Symbol publishing additional parameters + type: string + default: '' + + - name: artifactsPublishingAdditionalParameters + displayName: Artifact publishing additional parameters + type: string + default: '' + + - name: signingValidationAdditionalParameters + displayName: Signing validation additional parameters + type: string + default: '' + + # Which stages should finish execution before post-build stages start + - name: validateDependsOn + type: object + default: + - build + + - name: publishDependsOn + type: object + default: + - Validate + + # Optional: Call asset publishing rather than running in a separate stage + - name: publishAssetsImmediately + type: boolean + default: false + + - name: is1ESPipeline + type: boolean + default: false + +stages: +- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: + - stage: Validate + dependsOn: ${{ parameters.validateDependsOn }} + displayName: Validate Build Assets + variables: + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobs: + - job: + displayName: NuGet Validation + condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true')) + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: windows.vs2022.amd64 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ + + - job: + displayName: Signing Validation + condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + itemPattern: | + ** + !**/Microsoft.SourceBuild.Intermediate.*.nupkg + + # This is necessary whenever we want to publish/restore to an AzDO private feed + # Since sdk-task.ps1 tries to restore packages we need to do this authentication here + # otherwise it'll complain about accessing a private feed. + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to AzDO Feeds' + + # Signing validation will optionally work with the buildmanifest file which is downloaded from + # Azure DevOps above. + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task SigningValidation -restore -msbuildEngine vs + /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' + /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' + ${{ parameters.signingValidationAdditionalParameters }} + + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + StageLabel: 'Validation' + JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) + + - job: + displayName: SourceLink Validation + condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Blob Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: BlobArtifacts + checkDownloadedFiles: true + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 + arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ + -ExtractPath $(Agent.BuildDirectory)/Extract/ + -GHRepoName $(Build.Repository.Name) + -GHCommit $(Build.SourceVersion) + -SourcelinkCliVersion $(SourceLinkCLIVersion) + continueOnError: true + +- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: + - stage: publish_using_darc + ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: + dependsOn: ${{ parameters.publishDependsOn }} + ${{ else }}: + dependsOn: ${{ parameters.validateDependsOn }} + displayName: Publish using Darc + variables: + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobs: + - job: + displayName: Publish Using Darc + timeoutInMinutes: 120 + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: NetCore1ESPool-Publishing-Internal + image: windows.vs2019.amd64 + os: windows + ${{ else }}: + name: NetCore1ESPool-Publishing-Internal + demands: ImageOverride -equals windows.vs2019.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: NuGetAuthenticate@1 + + - task: PowerShell@2 + displayName: Publish Using Darc + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) + -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} + -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' + -MaestroToken '$(MaestroApiAccessToken)' + -WaitPublishingFinish true + -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' + -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/core-templates/post-build/setup-maestro-vars.yml b/eng/common/core-templates/post-build/setup-maestro-vars.yml new file mode 100644 index 000000000..8d56b5726 --- /dev/null +++ b/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -0,0 +1,74 @@ +parameters: + BARBuildId: '' + PromoteToChannelIds: '' + is1ESPipeline: '' + +steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Release Configs + inputs: + buildType: current + artifactName: ReleaseConfigs + checkDownloadedFiles: true + + - task: PowerShell@2 + name: setReleaseVars + displayName: Set Release Configs Vars + inputs: + targetType: inline + pwsh: true + script: | + try { + if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { + $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt + + $BarId = $Content | Select -Index 0 + $Channels = $Content | Select -Index 1 + $IsStableBuild = $Content | Select -Index 2 + + $AzureDevOpsProject = $Env:System_TeamProject + $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId + $AzureDevOpsBuildId = $Env:Build_BuildId + } + else { + $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" + + $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' + $apiHeaders.Add('Accept', 'application/json') + $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") + + $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } + + $BarId = $Env:BARBuildId + $Channels = $Env:PromoteToMaestroChannels -split "," + $Channels = $Channels -join "][" + $Channels = "[$Channels]" + + $IsStableBuild = $buildInfo.stable + $AzureDevOpsProject = $buildInfo.azureDevOpsProject + $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId + $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId + } + + Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId" + Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels" + Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild" + + Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject" + Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId" + Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId" + } + catch { + Write-Host $_ + Write-Host $_.Exception + Write-Host $_.ScriptStackTrace + exit 1 + } + env: + MAESTRO_API_TOKEN: $(MaestroApiAccessToken) + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} diff --git a/eng/common/core-templates/post-build/trigger-subscription.yml b/eng/common/core-templates/post-build/trigger-subscription.yml new file mode 100644 index 000000000..da669030d --- /dev/null +++ b/eng/common/core-templates/post-build/trigger-subscription.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Triggering subscriptions + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/trigger-subscriptions.ps1 + arguments: -SourceRepo $(Build.Repository.Uri) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/core-templates/steps/add-build-to-channel.yml b/eng/common/core-templates/steps/add-build-to-channel.yml new file mode 100644 index 000000000..f67a210d6 --- /dev/null +++ b/eng/common/core-templates/steps/add-build-to-channel.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Add Build to Channel + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1 + arguments: -BuildId $(BARBuildId) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroApiAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/core-templates/steps/component-governance.yml b/eng/common/core-templates/steps/component-governance.yml new file mode 100644 index 000000000..df449a34c --- /dev/null +++ b/eng/common/core-templates/steps/component-governance.yml @@ -0,0 +1,14 @@ +parameters: + disableComponentGovernance: false + componentGovernanceIgnoreDirectories: '' + is1ESPipeline: false + +steps: +- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: + - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" + displayName: Set skipComponentGovernanceDetection variable +- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: + - task: ComponentGovernanceComponentDetection@0 + continueOnError: true + inputs: + ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} \ No newline at end of file diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml new file mode 100644 index 000000000..d938b60e1 --- /dev/null +++ b/eng/common/core-templates/steps/generate-sbom.yml @@ -0,0 +1,54 @@ +# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. +# PackageName - The name of the package this SBOM represents. +# PackageVersion - The version of the package this SBOM represents. +# ManifestDirPath - The path of the directory where the generated manifest files will be placed +# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. + +parameters: + PackageVersion: 9.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageName: '.NET' + ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom + IgnoreDirectories: '' + sbomContinueOnError: true + is1ESPipeline: false + # disable publishArtifacts if some other step is publishing the artifacts (like job.yml). + publishArtifacts: true + +steps: +- task: PowerShell@2 + displayName: Prep for SBOM generation in (Non-linux) + condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) + inputs: + filePath: ./eng/common/generate-sbom-prep.ps1 + arguments: ${{parameters.manifestDirPath}} + +# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 +- script: | + chmod +x ./eng/common/generate-sbom-prep.sh + ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} + displayName: Prep for SBOM generation in (Linux) + condition: eq(variables['Agent.Os'], 'Linux') + continueOnError: ${{ parameters.sbomContinueOnError }} + +- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: 'Generate SBOM manifest' + continueOnError: ${{ parameters.sbomContinueOnError }} + inputs: + PackageName: ${{ parameters.packageName }} + BuildDropPath: ${{ parameters.buildDropPath }} + PackageVersion: ${{ parameters.packageVersion }} + ManifestDirPath: ${{ parameters.manifestDirPath }} + ${{ if ne(parameters.IgnoreDirectories, '') }}: + AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' + +- ${{ if eq(parameters.publishArtifacts, 'true')}}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish SBOM manifest + continueOnError: ${{parameters.sbomContinueOnError}} + targetPath: '${{ parameters.manifestDirPath }}' + artifactName: $(ARTIFACT_NAME) + diff --git a/eng/common/core-templates/steps/publish-build-artifacts.yml b/eng/common/core-templates/steps/publish-build-artifacts.yml new file mode 100644 index 000000000..f24ce3466 --- /dev/null +++ b/eng/common/core-templates/steps/publish-build-artifacts.yml @@ -0,0 +1,20 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false +- name: args + type: object + default: {} +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - template: /eng/common/templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ each parameter in parameters.args }}: + ${{ parameter.key }}: ${{ parameter.value }} +- ${{ else }}: + - template: /eng/common/templates-official/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ each parameter in parameters.args }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml new file mode 100644 index 000000000..8c5ea77b5 --- /dev/null +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -0,0 +1,59 @@ +parameters: + StageLabel: '' + JobLabel: '' + CustomSensitiveDataList: '' + # A default - in case value from eng/common/core-templates/post-build/common-variables.yml is not passed + BinlogToolVersion: '1.0.11' + is1ESPipeline: false + +steps: +- task: Powershell@2 + displayName: Prepare Binlogs to Upload + inputs: + targetType: inline + script: | + New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + continueOnError: true + condition: always() + +- task: PowerShell@2 + displayName: Redact Logs + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml + # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # If the file exists - sensitive data for redaction will be sourced from it + # (single entry per line, lines starting with '# ' are considered comments and skipped) + arguments: -InputPath '$(Build.SourcesDirectory)/PostBuildLogs' + -BinlogToolVersion ${{parameters.BinlogToolVersion}} + -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + '$(publishing-dnceng-devdiv-code-r-build-re)' + '$(MaestroAccessToken)' + '$(dn-bot-all-orgs-artifact-feeds-rw)' + '$(akams-client-id)' + '$(akams-client-secret)' + '$(microsoft-symbol-server-pat)' + '$(symweb-symbol-server-pat)' + '$(dn-bot-all-orgs-build-rw-code-rw)' + ${{parameters.CustomSensitiveDataList}} + continueOnError: true + condition: always() + +- task: CopyFiles@2 + displayName: Gather post build logs + inputs: + SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' + +- template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish Logs + pathToPublish: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' + publishLocation: Container + artifactName: PostBuildLogs + continueOnError: true + condition: always() diff --git a/eng/common/core-templates/steps/publish-pipeline-artifacts.yml b/eng/common/core-templates/steps/publish-pipeline-artifacts.yml new file mode 100644 index 000000000..2efec04dc --- /dev/null +++ b/eng/common/core-templates/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,20 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: args + type: object + default: {} + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - template: /eng/common/templates/steps/publish-pipeline-artifacts.yml + parameters: + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} +- ${{ else }}: + - template: /eng/common/templates-official/steps/publish-pipeline-artifacts.yml + parameters: + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/core-templates/steps/retain-build.yml b/eng/common/core-templates/steps/retain-build.yml new file mode 100644 index 000000000..83d97a26a --- /dev/null +++ b/eng/common/core-templates/steps/retain-build.yml @@ -0,0 +1,28 @@ +parameters: + # Optional azure devops PAT with build execute permissions for the build's organization, + # only needed if the build that should be retained ran on a different organization than + # the pipeline where this template is executing from + Token: '' + # Optional BuildId to retain, defaults to the current running build + BuildId: '' + # Azure devops Organization URI for the build in the https://dev.azure.com/ format. + # Defaults to the organization the current pipeline is running on + AzdoOrgUri: '$(System.CollectionUri)' + # Azure devops project for the build. Defaults to the project the current pipeline is running on + AzdoProject: '$(System.TeamProject)' + +steps: + - task: powershell@2 + inputs: + targetType: 'filePath' + filePath: eng/common/retain-build.ps1 + pwsh: true + arguments: > + -AzdoOrgUri: ${{parameters.AzdoOrgUri}} + -AzdoProject ${{parameters.AzdoProject}} + -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }} + -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}} + displayName: Enable permanent build retention + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + BUILD_ID: $(Build.BuildId) \ No newline at end of file diff --git a/eng/common/core-templates/steps/send-to-helix.yml b/eng/common/core-templates/steps/send-to-helix.yml new file mode 100644 index 000000000..68fa739c4 --- /dev/null +++ b/eng/common/core-templates/steps/send-to-helix.yml @@ -0,0 +1,93 @@ +# Please remember to update the documentation if you make changes to these parameters! +parameters: + HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ + HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' + HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number + HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues + HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group + HelixProjectPath: 'eng/common/helixpublish.proj' # optional -- path to the project file to build relative to BUILD_SOURCESDIRECTORY + HelixProjectArguments: '' # optional -- arguments passed to the build command + HelixConfiguration: '' # optional -- additional property attached to a job + HelixPreCommands: '' # optional -- commands to run before Helix work item execution + HelixPostCommands: '' # optional -- commands to run after Helix work item execution + WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects + WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects + WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects + CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload + XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true + XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects + XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects + XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner + XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects + IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion + DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json + DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json + WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." + IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set + HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net ) + Creator: '' # optional -- if the build is external, use this to specify who is sending the job + DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO + condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() + continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false + +steps: + - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' + displayName: ${{ parameters.DisplayNamePrefix }} (Windows) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + WorkItemCommand: ${{ parameters.WorkItemCommand }} + WorkItemTimeout: ${{ parameters.WorkItemTimeout }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + XUnitProjects: ${{ parameters.XUnitProjects }} + XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} + XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} + XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} + XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + HelixBaseUri: ${{ parameters.HelixBaseUri }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog + displayName: ${{ parameters.DisplayNamePrefix }} (Unix) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + WorkItemCommand: ${{ parameters.WorkItemCommand }} + WorkItemTimeout: ${{ parameters.WorkItemTimeout }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + XUnitProjects: ${{ parameters.XUnitProjects }} + XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} + XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} + XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} + XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + HelixBaseUri: ${{ parameters.HelixBaseUri }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml new file mode 100644 index 000000000..bdd725b49 --- /dev/null +++ b/eng/common/core-templates/steps/source-build.yml @@ -0,0 +1,134 @@ +parameters: + # This template adds arcade-powered source-build to CI. + + # This is a 'steps' template, and is intended for advanced scenarios where the existing build + # infra has a careful build methodology that must be followed. For example, a repo + # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline + # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to + # GitHub. Using this steps template leaves room for that infra to be included. + + # Defines the platform on which to run the steps. See 'eng/common/core-templates/job/source-build.yml' + # for details. The entire object is described in the 'job' template for simplicity, even though + # the usage of the properties on this object is split between the 'job' and 'steps' templates. + platform: {} + is1ESPipeline: false + +steps: +# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.) +- script: | + set -x + df -h + + # If building on the internal project, the artifact feeds variable may be available (usually only if needed) + # In that case, call the feed setup script to add internal feeds corresponding to public ones. + # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. + # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those + # changes. + internalRestoreArgs= + if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then + # Temporarily work around https://github.com/dotnet/arcade/issues/7709 + chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh + $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) + internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' + + # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. + # This only works if there is a username/email configured, which won't be the case in most CI runs. + git config --get user.email + if [ $? -ne 0 ]; then + git config user.email dn-bot@microsoft.com + git config user.name dn-bot + fi + fi + + # If building on the internal project, the internal storage variable may be available (usually only if needed) + # In that case, add variables to allow the download of internal runtimes if the specified versions are not found + # in the default public locations. + internalRuntimeDownloadArgs= + if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then + internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' + fi + + buildConfig=Release + # Check if AzDO substitutes in a build config from a variable, and use it if so. + if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then + buildConfig='$(_BuildConfig)' + fi + + officialBuildArgs= + if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then + officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' + fi + + targetRidArgs= + if [ '${{ parameters.platform.targetRID }}' != '' ]; then + targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' + fi + + runtimeOsArgs= + if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then + runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' + fi + + baseOsArgs= + if [ '${{ parameters.platform.baseOS }}' != '' ]; then + baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' + fi + + publishArgs= + if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then + publishArgs='--publish' + fi + + assetManifestFileName=SourceBuild_RidSpecific.xml + if [ '${{ parameters.platform.name }}' != '' ]; then + assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml + fi + + ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ + --configuration $buildConfig \ + --restore --build --pack $publishArgs -bl \ + $officialBuildArgs \ + $internalRuntimeDownloadArgs \ + $internalRestoreArgs \ + $targetRidArgs \ + $runtimeOsArgs \ + $baseOsArgs \ + /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ + /p:ArcadeBuildFromSource=true \ + /p:DotNetBuildSourceOnly=true \ + /p:DotNetBuildRepo=true \ + /p:AssetManifestFileName=$assetManifestFileName + displayName: Build + +# Upload build logs for diagnosis. +- task: CopyFiles@2 + displayName: Prepare BuildLogs staging directory + inputs: + SourceFolder: '$(Build.SourcesDirectory)' + Contents: | + **/*.log + **/*.binlog + artifacts/sb/prebuilt-report/** + TargetFolder: '$(Build.StagingDirectory)/BuildLogs' + CleanTargetFolder: true + continueOnError: true + condition: succeededOrFailed() + +- template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish BuildLogs + targetPath: '$(Build.StagingDirectory)/BuildLogs' + artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) + continueOnError: true + condition: succeededOrFailed() + +# Manually inject component detection so that we can ignore the source build upstream cache, which contains +# a nupkg cache of input packages (a local feed). +# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' +# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets +- task: ComponentGovernanceComponentDetection@0 + displayName: Component Detection (Exclude upstream cache) + inputs: + ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/sb/src/artifacts/obj/source-built-upstream-cache' diff --git a/eng/common/core-templates/variables/pool-providers.yml b/eng/common/core-templates/variables/pool-providers.yml new file mode 100644 index 000000000..41053d382 --- /dev/null +++ b/eng/common/core-templates/variables/pool-providers.yml @@ -0,0 +1,8 @@ +parameters: + is1ESPipeline: false + +variables: + - ${{ if eq(parameters.is1ESPipeline, 'true') }}: + - template: /eng/common/templates-official/variables/pool-providers.yml + - ${{ else }}: + - template: /eng/common/templates/variables/pool-providers.yml \ No newline at end of file diff --git a/eng/common/cross/arm/sources.list.bionic b/eng/common/cross/arm/sources.list.bionic new file mode 100644 index 000000000..210955740 --- /dev/null +++ b/eng/common/cross/arm/sources.list.bionic @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse diff --git a/eng/common/cross/arm/sources.list.focal b/eng/common/cross/arm/sources.list.focal new file mode 100644 index 000000000..4de2600c1 --- /dev/null +++ b/eng/common/cross/arm/sources.list.focal @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ focal main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ focal-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal-security main restricted universe multiverse diff --git a/eng/common/cross/arm/sources.list.jammy b/eng/common/cross/arm/sources.list.jammy new file mode 100644 index 000000000..6bb045302 --- /dev/null +++ b/eng/common/cross/arm/sources.list.jammy @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ jammy main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ jammy-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ jammy-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ jammy-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy-security main restricted universe multiverse diff --git a/eng/common/cross/arm/sources.list.jessie b/eng/common/cross/arm/sources.list.jessie new file mode 100644 index 000000000..4d142ac9b --- /dev/null +++ b/eng/common/cross/arm/sources.list.jessie @@ -0,0 +1,3 @@ +# Debian (sid) # UNSTABLE +deb http://ftp.debian.org/debian/ sid main contrib non-free +deb-src http://ftp.debian.org/debian/ sid main contrib non-free diff --git a/eng/common/cross/arm/sources.list.xenial b/eng/common/cross/arm/sources.list.xenial new file mode 100644 index 000000000..56fbb36a5 --- /dev/null +++ b/eng/common/cross/arm/sources.list.xenial @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ xenial main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-security main restricted universe multiverse diff --git a/eng/common/cross/arm/sources.list.zesty b/eng/common/cross/arm/sources.list.zesty new file mode 100644 index 000000000..ea2c14a78 --- /dev/null +++ b/eng/common/cross/arm/sources.list.zesty @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ zesty main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ zesty-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ zesty-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ zesty-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty-security main restricted universe multiverse diff --git a/eng/common/cross/arm/tizen/tizen.patch b/eng/common/cross/arm/tizen/tizen.patch new file mode 100644 index 000000000..fb12ade72 --- /dev/null +++ b/eng/common/cross/arm/tizen/tizen.patch @@ -0,0 +1,9 @@ +diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so +--- a/usr/lib/libc.so 2016-12-30 23:00:08.284951863 +0900 ++++ b/usr/lib/libc.so 2016-12-30 23:00:32.140951815 +0900 +@@ -2,4 +2,4 @@ + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ + OUTPUT_FORMAT(elf32-littlearm) +-GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux-armhf.so.3 ) ) ++GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-armhf.so.3 ) ) diff --git a/eng/common/cross/arm64/sources.list.bionic b/eng/common/cross/arm64/sources.list.bionic new file mode 100644 index 000000000..210955740 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.bionic @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse diff --git a/eng/common/cross/arm64/sources.list.buster b/eng/common/cross/arm64/sources.list.buster new file mode 100644 index 000000000..7194ac64a --- /dev/null +++ b/eng/common/cross/arm64/sources.list.buster @@ -0,0 +1,11 @@ +deb http://deb.debian.org/debian buster main +deb-src http://deb.debian.org/debian buster main + +deb http://deb.debian.org/debian-security/ buster/updates main +deb-src http://deb.debian.org/debian-security/ buster/updates main + +deb http://deb.debian.org/debian buster-updates main +deb-src http://deb.debian.org/debian buster-updates main + +deb http://deb.debian.org/debian buster-backports main contrib non-free +deb-src http://deb.debian.org/debian buster-backports main contrib non-free diff --git a/eng/common/cross/arm64/sources.list.focal b/eng/common/cross/arm64/sources.list.focal new file mode 100644 index 000000000..4de2600c1 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.focal @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ focal main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ focal-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ focal-security main restricted universe multiverse diff --git a/eng/common/cross/arm64/sources.list.jammy b/eng/common/cross/arm64/sources.list.jammy new file mode 100644 index 000000000..6bb045302 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.jammy @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ jammy main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ jammy-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ jammy-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ jammy-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ jammy-security main restricted universe multiverse diff --git a/eng/common/cross/arm64/sources.list.stretch b/eng/common/cross/arm64/sources.list.stretch new file mode 100644 index 000000000..0e1215774 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.stretch @@ -0,0 +1,12 @@ +deb http://deb.debian.org/debian stretch main +deb-src http://deb.debian.org/debian stretch main + +deb http://deb.debian.org/debian-security/ stretch/updates main +deb-src http://deb.debian.org/debian-security/ stretch/updates main + +deb http://deb.debian.org/debian stretch-updates main +deb-src http://deb.debian.org/debian stretch-updates main + +deb http://deb.debian.org/debian stretch-backports main contrib non-free +deb-src http://deb.debian.org/debian stretch-backports main contrib non-free + diff --git a/eng/common/cross/arm64/sources.list.xenial b/eng/common/cross/arm64/sources.list.xenial new file mode 100644 index 000000000..56fbb36a5 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.xenial @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ xenial main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-security main restricted universe multiverse diff --git a/eng/common/cross/arm64/sources.list.zesty b/eng/common/cross/arm64/sources.list.zesty new file mode 100644 index 000000000..ea2c14a78 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.zesty @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ zesty main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ zesty-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ zesty-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ zesty-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ zesty-security main restricted universe multiverse diff --git a/eng/common/cross/arm64/tizen/tizen.patch b/eng/common/cross/arm64/tizen/tizen.patch new file mode 100644 index 000000000..af7c8be05 --- /dev/null +++ b/eng/common/cross/arm64/tizen/tizen.patch @@ -0,0 +1,9 @@ +diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so +--- a/usr/lib64/libc.so 2016-12-30 23:00:08.284951863 +0900 ++++ b/usr/lib64/libc.so 2016-12-30 23:00:32.140951815 +0900 +@@ -2,4 +2,4 @@ + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ + OUTPUT_FORMAT(elf64-littleaarch64) +-GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib/ld-linux-aarch64.so.1 ) ) ++GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-aarch64.so.1 ) ) diff --git a/eng/common/cross/armel/armel.jessie.patch b/eng/common/cross/armel/armel.jessie.patch new file mode 100644 index 000000000..2d2615619 --- /dev/null +++ b/eng/common/cross/armel/armel.jessie.patch @@ -0,0 +1,43 @@ +diff -u -r a/usr/include/urcu/uatomic/generic.h b/usr/include/urcu/uatomic/generic.h +--- a/usr/include/urcu/uatomic/generic.h 2014-10-22 15:00:58.000000000 -0700 ++++ b/usr/include/urcu/uatomic/generic.h 2020-10-30 21:38:28.550000000 -0700 +@@ -69,10 +69,10 @@ + #endif + #ifdef UATOMIC_HAS_ATOMIC_SHORT + case 2: +- return __sync_val_compare_and_swap_2(addr, old, _new); ++ return __sync_val_compare_and_swap_2((uint16_t*) addr, old, _new); + #endif + case 4: +- return __sync_val_compare_and_swap_4(addr, old, _new); ++ return __sync_val_compare_and_swap_4((uint32_t*) addr, old, _new); + #if (CAA_BITS_PER_LONG == 64) + case 8: + return __sync_val_compare_and_swap_8(addr, old, _new); +@@ -109,7 +109,7 @@ + return; + #endif + case 4: +- __sync_and_and_fetch_4(addr, val); ++ __sync_and_and_fetch_4((uint32_t*) addr, val); + return; + #if (CAA_BITS_PER_LONG == 64) + case 8: +@@ -148,7 +148,7 @@ + return; + #endif + case 4: +- __sync_or_and_fetch_4(addr, val); ++ __sync_or_and_fetch_4((uint32_t*) addr, val); + return; + #if (CAA_BITS_PER_LONG == 64) + case 8: +@@ -187,7 +187,7 @@ + return __sync_add_and_fetch_2(addr, val); + #endif + case 4: +- return __sync_add_and_fetch_4(addr, val); ++ return __sync_add_and_fetch_4((uint32_t*) addr, val); + #if (CAA_BITS_PER_LONG == 64) + case 8: + return __sync_add_and_fetch_8(addr, val); diff --git a/eng/common/cross/armel/sources.list.jessie b/eng/common/cross/armel/sources.list.jessie new file mode 100644 index 000000000..3d9c3059d --- /dev/null +++ b/eng/common/cross/armel/sources.list.jessie @@ -0,0 +1,3 @@ +# Debian (jessie) # Stable +deb http://ftp.debian.org/debian/ jessie main contrib non-free +deb-src http://ftp.debian.org/debian/ jessie main contrib non-free diff --git a/eng/common/cross/armel/tizen/tizen.patch b/eng/common/cross/armel/tizen/tizen.patch new file mode 100644 index 000000000..ca7c7c1ff --- /dev/null +++ b/eng/common/cross/armel/tizen/tizen.patch @@ -0,0 +1,9 @@ +diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so +--- a/usr/lib/libc.so 2016-12-30 23:00:08.284951863 +0900 ++++ b/usr/lib/libc.so 2016-12-30 23:00:32.140951815 +0900 +@@ -2,4 +2,4 @@ + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ + OUTPUT_FORMAT(elf32-littlearm) +-GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux.so.3 ) ) ++GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux.so.3 ) ) diff --git a/eng/common/cross/armv6/sources.list.buster b/eng/common/cross/armv6/sources.list.buster new file mode 100644 index 000000000..f27fc4fb3 --- /dev/null +++ b/eng/common/cross/armv6/sources.list.buster @@ -0,0 +1,2 @@ +deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi +deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi diff --git a/eng/common/cross/build-android-rootfs.sh b/eng/common/cross/build-android-rootfs.sh new file mode 100755 index 000000000..f163fb9da --- /dev/null +++ b/eng/common/cross/build-android-rootfs.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +set -e +__NDK_Version=r21 + +usage() +{ + echo "Creates a toolchain and sysroot used for cross-compiling for Android." + echo. + echo "Usage: $0 [BuildArch] [ApiLevel]" + echo. + echo "BuildArch is the target architecture of Android. Currently only arm64 is supported." + echo "ApiLevel is the target Android API level. API levels usually match to Android releases. See https://source.android.com/source/build-numbers.html" + echo. + echo "By default, the toolchain and sysroot will be generated in cross/android-rootfs/toolchain/[BuildArch]. You can change this behavior" + echo "by setting the TOOLCHAIN_DIR environment variable" + echo. + echo "By default, the NDK will be downloaded into the cross/android-rootfs/android-ndk-$__NDK_Version directory. If you already have an NDK installation," + echo "you can set the NDK_DIR environment variable to have this script use that installation of the NDK." + echo "By default, this script will generate a file, android_platform, in the root of the ROOTFS_DIR directory that contains the RID for the supported and tested Android build: android.28-arm64. This file is to replace '/etc/os-release', which is not available for Android." + exit 1 +} + +__ApiLevel=28 # The minimum platform for arm64 is API level 21 but the minimum version that support glob(3) is 28. See $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/glob.h +__BuildArch=arm64 +__AndroidArch=aarch64 +__AndroidToolchain=aarch64-linux-android + +for i in "$@" + do + lowerI="$(echo $i | tr "[:upper:]" "[:lower:]")" + case $lowerI in + -?|-h|--help) + usage + exit 1 + ;; + arm64) + __BuildArch=arm64 + __AndroidArch=aarch64 + __AndroidToolchain=aarch64-linux-android + ;; + arm) + __BuildArch=arm + __AndroidArch=arm + __AndroidToolchain=arm-linux-androideabi + ;; + *[0-9]) + __ApiLevel=$i + ;; + *) + __UnprocessedBuildArgs="$__UnprocessedBuildArgs $i" + ;; + esac +done + +# Obtain the location of the bash script to figure out where the root of the repo is. +__ScriptBaseDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +__CrossDir="$__ScriptBaseDir/../../../.tools/android-rootfs" + +if [[ ! -f "$__CrossDir" ]]; then + mkdir -p "$__CrossDir" +fi + +# Resolve absolute path to avoid `../` in build logs +__CrossDir="$( cd "$__CrossDir" && pwd )" + +__NDK_Dir="$__CrossDir/android-ndk-$__NDK_Version" +__lldb_Dir="$__CrossDir/lldb" +__ToolchainDir="$__CrossDir/android-ndk-$__NDK_Version" + +if [[ -n "$TOOLCHAIN_DIR" ]]; then + __ToolchainDir=$TOOLCHAIN_DIR +fi + +if [[ -n "$NDK_DIR" ]]; then + __NDK_Dir=$NDK_DIR +fi + +echo "Target API level: $__ApiLevel" +echo "Target architecture: $__BuildArch" +echo "NDK location: $__NDK_Dir" +echo "Target Toolchain location: $__ToolchainDir" + +# Download the NDK if required +if [ ! -d $__NDK_Dir ]; then + echo Downloading the NDK into $__NDK_Dir + mkdir -p $__NDK_Dir + wget -q --progress=bar:force:noscroll --show-progress https://dl.google.com/android/repository/android-ndk-$__NDK_Version-linux-x86_64.zip -O $__CrossDir/android-ndk-$__NDK_Version-linux-x86_64.zip + unzip -q $__CrossDir/android-ndk-$__NDK_Version-linux-x86_64.zip -d $__CrossDir +fi + +if [ ! -d $__lldb_Dir ]; then + mkdir -p $__lldb_Dir + echo Downloading LLDB into $__lldb_Dir + wget -q --progress=bar:force:noscroll --show-progress https://dl.google.com/android/repository/lldb-2.3.3614996-linux-x86_64.zip -O $__CrossDir/lldb-2.3.3614996-linux-x86_64.zip + unzip -q $__CrossDir/lldb-2.3.3614996-linux-x86_64.zip -d $__lldb_Dir +fi + +echo "Download dependencies..." +__TmpDir=$__CrossDir/tmp/$__BuildArch/ +mkdir -p "$__TmpDir" + +# combined dependencies for coreclr, installer and libraries +__AndroidPackages="libicu" +__AndroidPackages+=" libandroid-glob" +__AndroidPackages+=" liblzma" +__AndroidPackages+=" krb5" +__AndroidPackages+=" openssl" + +for path in $(wget -qO- https://packages.termux.dev/termux-main-21/dists/stable/main/binary-$__AndroidArch/Packages |\ + grep -A15 "Package: \(${__AndroidPackages// /\\|}\)" | grep -v "static\|tool" | grep Filename); do + + if [[ "$path" != "Filename:" ]]; then + echo "Working on: $path" + wget -qO- https://packages.termux.dev/termux-main-21/$path | dpkg -x - "$__TmpDir" + fi +done + +cp -R "$__TmpDir/data/data/com.termux/files/usr/"* "$__ToolchainDir/sysroot/usr/" + +# Generate platform file for build.sh script to assign to __DistroRid +echo "Generating platform file..." +echo "RID=android.${__ApiLevel}-${__BuildArch}" > $__ToolchainDir/sysroot/android_platform + +echo "Now to build coreclr, libraries and installers; run:" +echo ROOTFS_DIR=\$\(realpath $__ToolchainDir/sysroot\) ./build.sh --cross --arch $__BuildArch \ + --subsetCategory coreclr +echo ROOTFS_DIR=\$\(realpath $__ToolchainDir/sysroot\) ./build.sh --cross --arch $__BuildArch \ + --subsetCategory libraries +echo ROOTFS_DIR=\$\(realpath $__ToolchainDir/sysroot\) ./build.sh --cross --arch $__BuildArch \ + --subsetCategory installer diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh new file mode 100755 index 000000000..a8e35df7c --- /dev/null +++ b/eng/common/cross/build-rootfs.sh @@ -0,0 +1,751 @@ +#!/usr/bin/env bash + +set -e + +usage() +{ + echo "Usage: $0 [BuildArch] [CodeName] [lldbx.y] [llvmx[.y]] [--skipunmount] --rootfsdir ]" + echo "BuildArch can be: arm(default), arm64, armel, armv6, ppc64le, riscv64, s390x, x64, x86" + echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine" + echo " for alpine can be specified with version: alpineX.YY or alpineedge" + echo " for FreeBSD can be: freebsd13, freebsd14" + echo " for illumos can be: illumos" + echo " for Haiku can be: haiku." + echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD" + echo "llvmx[.y] - optional, LLVM version for LLVM related packages." + echo "--skipunmount - optional, will skip the unmount of rootfs folder." + echo "--skipsigcheck - optional, will skip package signature checks (allowing untrusted packages)." + echo "--use-mirror - optional, use mirror URL to fetch resources, when available." + echo "--jobs N - optional, restrict to N jobs." + exit 1 +} + +__CodeName=xenial +__CrossDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +__BuildArch=arm +__AlpineArch=armv7 +__FreeBSDArch=arm +__FreeBSDMachineArch=armv7 +__IllumosArch=arm7 +__HaikuArch=arm +__QEMUArch=arm +__UbuntuArch=armhf +__UbuntuRepo="http://ports.ubuntu.com/" +__LLDB_Package="liblldb-3.9-dev" +__SkipUnmount=0 + +# base development support +__UbuntuPackages="build-essential" + +__AlpinePackages="alpine-base" +__AlpinePackages+=" build-base" +__AlpinePackages+=" linux-headers" +__AlpinePackages+=" lldb-dev" +__AlpinePackages+=" python3" +__AlpinePackages+=" libedit" + +# symlinks fixer +__UbuntuPackages+=" symlinks" + +# runtime dependencies +__UbuntuPackages+=" libicu-dev" +__UbuntuPackages+=" liblttng-ust-dev" +__UbuntuPackages+=" libunwind8-dev" +__UbuntuPackages+=" libnuma-dev" + +__AlpinePackages+=" gettext-dev" +__AlpinePackages+=" icu-dev" +__AlpinePackages+=" libunwind-dev" +__AlpinePackages+=" lttng-ust-dev" +__AlpinePackages+=" compiler-rt" +__AlpinePackages+=" numactl-dev" + +# runtime libraries' dependencies +__UbuntuPackages+=" libcurl4-openssl-dev" +__UbuntuPackages+=" libkrb5-dev" +__UbuntuPackages+=" libssl-dev" +__UbuntuPackages+=" zlib1g-dev" + +__AlpinePackages+=" curl-dev" +__AlpinePackages+=" krb5-dev" +__AlpinePackages+=" openssl-dev" +__AlpinePackages+=" zlib-dev" + +__FreeBSDBase="13.2-RELEASE" +__FreeBSDPkg="1.17.0" +__FreeBSDABI="13" +__FreeBSDPackages="libunwind" +__FreeBSDPackages+=" icu" +__FreeBSDPackages+=" libinotify" +__FreeBSDPackages+=" openssl" +__FreeBSDPackages+=" krb5" +__FreeBSDPackages+=" terminfo-db" + +__IllumosPackages="icu" +__IllumosPackages+=" mit-krb5" +__IllumosPackages+=" openssl" +__IllumosPackages+=" zlib" + +__HaikuPackages="gcc_syslibs" +__HaikuPackages+=" gcc_syslibs_devel" +__HaikuPackages+=" gmp" +__HaikuPackages+=" gmp_devel" +__HaikuPackages+=" icu66" +__HaikuPackages+=" icu66_devel" +__HaikuPackages+=" krb5" +__HaikuPackages+=" krb5_devel" +__HaikuPackages+=" libiconv" +__HaikuPackages+=" libiconv_devel" +__HaikuPackages+=" llvm12_libunwind" +__HaikuPackages+=" llvm12_libunwind_devel" +__HaikuPackages+=" mpfr" +__HaikuPackages+=" mpfr_devel" +__HaikuPackages+=" openssl" +__HaikuPackages+=" openssl_devel" +__HaikuPackages+=" zlib" +__HaikuPackages+=" zlib_devel" + +# ML.NET dependencies +__UbuntuPackages+=" libomp5" +__UbuntuPackages+=" libomp-dev" + +# Taken from https://github.com/alpinelinux/alpine-chroot-install/blob/6d08f12a8a70dd9b9dc7d997c88aa7789cc03c42/alpine-chroot-install#L85-L133 +__AlpineKeys=' +4a6a0840:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1yHJxQgsHQREclQu4Ohe\nqxTxd1tHcNnvnQTu/UrTky8wWvgXT+jpveroeWWnzmsYlDI93eLI2ORakxb3gA2O\nQ0Ry4ws8vhaxLQGC74uQR5+/yYrLuTKydFzuPaS1dK19qJPXB8GMdmFOijnXX4SA\njixuHLe1WW7kZVtjL7nufvpXkWBGjsfrvskdNA/5MfxAeBbqPgaq0QMEfxMAn6/R\nL5kNepi/Vr4S39Xvf2DzWkTLEK8pcnjNkt9/aafhWqFVW7m3HCAII6h/qlQNQKSo\nGuH34Q8GsFG30izUENV9avY7hSLq7nggsvknlNBZtFUcmGoQrtx3FmyYsIC8/R+B\nywIDAQAB +5243ef4b:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNijDxJ8kloskKQpJdx+\nmTMVFFUGDoDCbulnhZMJoKNkSuZOzBoFC94omYPtxnIcBdWBGnrm6ncbKRlR+6oy\nDO0W7c44uHKCFGFqBhDasdI4RCYP+fcIX/lyMh6MLbOxqS22TwSLhCVjTyJeeH7K\naA7vqk+QSsF4TGbYzQDDpg7+6aAcNzg6InNePaywA6hbT0JXbxnDWsB+2/LLSF2G\nmnhJlJrWB1WGjkz23ONIWk85W4S0XB/ewDefd4Ly/zyIciastA7Zqnh7p3Ody6Q0\nsS2MJzo7p3os1smGjUF158s6m/JbVh4DN6YIsxwl2OjDOz9R0OycfJSDaBVIGZzg\ncQIDAQAB +524d27bb:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr8s1q88XpuJWLCZALdKj\nlN8wg2ePB2T9aIcaxryYE/Jkmtu+ZQ5zKq6BT3y/udt5jAsMrhHTwroOjIsF9DeG\ne8Y3vjz+Hh4L8a7hZDaw8jy3CPag47L7nsZFwQOIo2Cl1SnzUc6/owoyjRU7ab0p\niWG5HK8IfiybRbZxnEbNAfT4R53hyI6z5FhyXGS2Ld8zCoU/R4E1P0CUuXKEN4p0\n64dyeUoOLXEWHjgKiU1mElIQj3k/IF02W89gDj285YgwqA49deLUM7QOd53QLnx+\nxrIrPv3A+eyXMFgexNwCKQU9ZdmWa00MjjHlegSGK8Y2NPnRoXhzqSP9T9i2HiXL\nVQIDAQAB +5261cecb:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwlzMkl7b5PBdfMzGdCT0\ncGloRr5xGgVmsdq5EtJvFkFAiN8Ac9MCFy/vAFmS8/7ZaGOXoCDWbYVLTLOO2qtX\nyHRl+7fJVh2N6qrDDFPmdgCi8NaE+3rITWXGrrQ1spJ0B6HIzTDNEjRKnD4xyg4j\ng01FMcJTU6E+V2JBY45CKN9dWr1JDM/nei/Pf0byBJlMp/mSSfjodykmz4Oe13xB\nCa1WTwgFykKYthoLGYrmo+LKIGpMoeEbY1kuUe04UiDe47l6Oggwnl+8XD1MeRWY\nsWgj8sF4dTcSfCMavK4zHRFFQbGp/YFJ/Ww6U9lA3Vq0wyEI6MCMQnoSMFwrbgZw\nwwIDAQAB +58199dcc:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3v8/ye/V/t5xf4JiXLXa\nhWFRozsnmn3hobON20GdmkrzKzO/eUqPOKTpg2GtvBhK30fu5oY5uN2ORiv2Y2ht\neLiZ9HVz3XP8Fm9frha60B7KNu66FO5P2o3i+E+DWTPqqPcCG6t4Znk2BypILcit\nwiPKTsgbBQR2qo/cO01eLLdt6oOzAaF94NH0656kvRewdo6HG4urbO46tCAizvCR\nCA7KGFMyad8WdKkTjxh8YLDLoOCtoZmXmQAiwfRe9pKXRH/XXGop8SYptLqyVVQ+\ntegOD9wRs2tOlgcLx4F/uMzHN7uoho6okBPiifRX+Pf38Vx+ozXh056tjmdZkCaV\naQIDAQAB +58cbb476:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoSPnuAGKtRIS5fEgYPXD\n8pSGvKAmIv3A08LBViDUe+YwhilSHbYXUEAcSH1KZvOo1WT1x2FNEPBEFEFU1Eyc\n+qGzbA03UFgBNvArurHQ5Z/GngGqE7IarSQFSoqewYRtFSfp+TL9CUNBvM0rT7vz\n2eMu3/wWG+CBmb92lkmyWwC1WSWFKO3x8w+Br2IFWvAZqHRt8oiG5QtYvcZL6jym\nY8T6sgdDlj+Y+wWaLHs9Fc+7vBuyK9C4O1ORdMPW15qVSl4Lc2Wu1QVwRiKnmA+c\nDsH/m7kDNRHM7TjWnuj+nrBOKAHzYquiu5iB3Qmx+0gwnrSVf27Arc3ozUmmJbLj\nzQIDAQAB +58e4f17d:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvBxJN9ErBgdRcPr5g4hV\nqyUSGZEKuvQliq2Z9SRHLh2J43+EdB6A+yzVvLnzcHVpBJ+BZ9RV30EM9guck9sh\nr+bryZcRHyjG2wiIEoduxF2a8KeWeQH7QlpwGhuobo1+gA8L0AGImiA6UP3LOirl\nI0G2+iaKZowME8/tydww4jx5vG132JCOScMjTalRsYZYJcjFbebQQolpqRaGB4iG\nWqhytWQGWuKiB1A22wjmIYf3t96l1Mp+FmM2URPxD1gk/BIBnX7ew+2gWppXOK9j\n1BJpo0/HaX5XoZ/uMqISAAtgHZAqq+g3IUPouxTphgYQRTRYpz2COw3NF43VYQrR\nbQIDAQAB +60ac2099:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwR4uJVtJOnOFGchnMW5Y\nj5/waBdG1u5BTMlH+iQMcV5+VgWhmpZHJCBz3ocD+0IGk2I68S5TDOHec/GSC0lv\n6R9o6F7h429GmgPgVKQsc8mPTPtbjJMuLLs4xKc+viCplXc0Nc0ZoHmCH4da6fCV\ntdpHQjVe6F9zjdquZ4RjV6R6JTiN9v924dGMAkbW/xXmamtz51FzondKC52Gh8Mo\n/oA0/T0KsCMCi7tb4QNQUYrf+Xcha9uus4ww1kWNZyfXJB87a2kORLiWMfs2IBBJ\nTmZ2Fnk0JnHDb8Oknxd9PvJPT0mvyT8DA+KIAPqNvOjUXP4bnjEHJcoCP9S5HkGC\nIQIDAQAB +6165ee59:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAutQkua2CAig4VFSJ7v54\nALyu/J1WB3oni7qwCZD3veURw7HxpNAj9hR+S5N/pNeZgubQvJWyaPuQDm7PTs1+\ntFGiYNfAsiibX6Rv0wci3M+z2XEVAeR9Vzg6v4qoofDyoTbovn2LztaNEjTkB+oK\ntlvpNhg1zhou0jDVYFniEXvzjckxswHVb8cT0OMTKHALyLPrPOJzVtM9C1ew2Nnc\n3848xLiApMu3NBk0JqfcS3Bo5Y2b1FRVBvdt+2gFoKZix1MnZdAEZ8xQzL/a0YS5\nHd0wj5+EEKHfOd3A75uPa/WQmA+o0cBFfrzm69QDcSJSwGpzWrD1ScH3AK8nWvoj\nv7e9gukK/9yl1b4fQQ00vttwJPSgm9EnfPHLAtgXkRloI27H6/PuLoNvSAMQwuCD\nhQRlyGLPBETKkHeodfLoULjhDi1K2gKJTMhtbnUcAA7nEphkMhPWkBpgFdrH+5z4\nLxy+3ek0cqcI7K68EtrffU8jtUj9LFTUC8dERaIBs7NgQ/LfDbDfGh9g6qVj1hZl\nk9aaIPTm/xsi8v3u+0qaq7KzIBc9s59JOoA8TlpOaYdVgSQhHHLBaahOuAigH+VI\nisbC9vmqsThF2QdDtQt37keuqoda2E6sL7PUvIyVXDRfwX7uMDjlzTxHTymvq2Ck\nhtBqojBnThmjJQFgZXocHG8CAwEAAQ== +61666e3f:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlEyxkHggKCXC2Wf5Mzx4\nnZLFZvU2bgcA3exfNPO/g1YunKfQY+Jg4fr6tJUUTZ3XZUrhmLNWvpvSwDS19ZmC\nIXOu0+V94aNgnhMsk9rr59I8qcbsQGIBoHzuAl8NzZCgdbEXkiY90w1skUw8J57z\nqCsMBydAueMXuWqF5nGtYbi5vHwK42PffpiZ7G5Kjwn8nYMW5IZdL6ZnMEVJUWC9\nI4waeKg0yskczYDmZUEAtrn3laX9677ToCpiKrvmZYjlGl0BaGp3cxggP2xaDbUq\nqfFxWNgvUAb3pXD09JM6Mt6HSIJaFc9vQbrKB9KT515y763j5CC2KUsilszKi3mB\nHYe5PoebdjS7D1Oh+tRqfegU2IImzSwW3iwA7PJvefFuc/kNIijfS/gH/cAqAK6z\nbhdOtE/zc7TtqW2Wn5Y03jIZdtm12CxSxwgtCF1NPyEWyIxAQUX9ACb3M0FAZ61n\nfpPrvwTaIIxxZ01L3IzPLpbc44x/DhJIEU+iDt6IMTrHOphD9MCG4631eIdB0H1b\n6zbNX1CXTsafqHRFV9XmYYIeOMggmd90s3xIbEujA6HKNP/gwzO6CDJ+nHFDEqoF\nSkxRdTkEqjTjVKieURW7Swv7zpfu5PrsrrkyGnsRrBJJzXlm2FOOxnbI2iSL1B5F\nrO5kbUxFeZUIDq+7Yv4kLWcCAwEAAQ== +616a9724:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnC+bR4bHf/L6QdU4puhQ\ngl1MHePszRC38bzvVFDUJsmCaMCL2suCs2A2yxAgGb9pu9AJYLAmxQC4mM3jNqhg\n/E7yuaBbek3O02zN/ctvflJ250wZCy+z0ZGIp1ak6pu1j14IwHokl9j36zNfGtfv\nADVOcdpWITFFlPqwq1qt/H3UsKVmtiF3BNWWTeUEQwKvlU8ymxgS99yn0+4OPyNT\nL3EUeS+NQJtDS01unau0t7LnjUXn+XIneWny8bIYOQCuVR6s/gpIGuhBaUqwaJOw\n7jkJZYF2Ij7uPb4b5/R3vX2FfxxqEHqssFSg8FFUNTZz3qNZs0CRVyfA972g9WkJ\nhPfn31pQYil4QGRibCMIeU27YAEjXoqfJKEPh4UWMQsQLrEfdGfb8VgwrPbniGfU\nL3jKJR3VAafL9330iawzVQDlIlwGl6u77gEXMl9K0pfazunYhAp+BMP+9ot5ckK+\nosmrqj11qMESsAj083GeFdfV3pXEIwUytaB0AKEht9DbqUfiE/oeZ/LAXgySMtVC\nsbC4ESmgVeY2xSBIJdDyUap7FR49GGrw0W49NUv9gRgQtGGaNVQQO9oGL2PBC41P\niWF9GLoX30HIz1P8PF/cZvicSSPkQf2Z6TV+t0ebdGNS5DjapdnCrq8m9Z0pyKsQ\nuxAL2a7zX8l5i1CZh1ycUGsCAwEAAQ== +616abc23:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0MfCDrhODRCIxR9Dep1s\neXafh5CE5BrF4WbCgCsevyPIdvTeyIaW4vmO3bbG4VzhogDZju+R3IQYFuhoXP5v\nY+zYJGnwrgz3r5wYAvPnLEs1+dtDKYOgJXQj+wLJBW1mzRDL8FoRXOe5iRmn1EFS\nwZ1DoUvyu7/J5r0itKicZp3QKED6YoilXed+1vnS4Sk0mzN4smuMR9eO1mMCqNp9\n9KTfRDHTbakIHwasECCXCp50uXdoW6ig/xUAFanpm9LtK6jctNDbXDhQmgvAaLXZ\nLvFqoaYJ/CvWkyYCgL6qxvMvVmPoRv7OPcyni4xR/WgWa0MSaEWjgPx3+yj9fiMA\n1S02pFWFDOr5OUF/O4YhFJvUCOtVsUPPfA/Lj6faL0h5QI9mQhy5Zb9TTaS9jB6p\nLw7u0dJlrjFedk8KTJdFCcaGYHP6kNPnOxMylcB/5WcztXZVQD5WpCicGNBxCGMm\nW64SgrV7M07gQfL/32QLsdqPUf0i8hoVD8wfQ3EpbQzv6Fk1Cn90bZqZafg8XWGY\nwddhkXk7egrr23Djv37V2okjzdqoyLBYBxMz63qQzFoAVv5VoY2NDTbXYUYytOvG\nGJ1afYDRVWrExCech1mX5ZVUB1br6WM+psFLJFoBFl6mDmiYt0vMYBddKISsvwLl\nIJQkzDwtXzT2cSjoj3T5QekCAwEAAQ== +616ac3bc:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvaaoSLab+IluixwKV5Od\n0gib2YurjPatGIbn5Ov2DLUFYiebj2oJINXJSwUOO+4WcuHFEqiL/1rya+k5hLZt\nhnPL1tn6QD4rESznvGSasRCQNT2vS/oyZbTYJRyAtFkEYLlq0t3S3xBxxHWuvIf0\nqVxVNYpQWyM3N9RIeYBR/euXKJXileSHk/uq1I5wTC0XBIHWcthczGN0m9wBEiWS\n0m3cnPk4q0Ea8mUJ91Rqob19qETz6VbSPYYpZk3qOycjKosuwcuzoMpwU8KRiMFd\n5LHtX0Hx85ghGsWDVtS0c0+aJa4lOMGvJCAOvDfqvODv7gKlCXUpgumGpLdTmaZ8\n1RwqspAe3IqBcdKTqRD4m2mSg23nVx2FAY3cjFvZQtfooT7q1ItRV5RgH6FhQSl7\n+6YIMJ1Bf8AAlLdRLpg+doOUGcEn+pkDiHFgI8ylH1LKyFKw+eXaAml/7DaWZk1d\ndqggwhXOhc/UUZFQuQQ8A8zpA13PcbC05XxN2hyP93tCEtyynMLVPtrRwDnHxFKa\nqKzs3rMDXPSXRn3ZZTdKH3069ApkEjQdpcwUh+EmJ1Ve/5cdtzT6kKWCjKBFZP/s\n91MlRrX2BTRdHaU5QJkUheUtakwxuHrdah2F94lRmsnQlpPr2YseJu6sIE+Dnx4M\nCfhdVbQL2w54R645nlnohu8CAwEAAQ== +616adfeb:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq0BFD1D4lIxQcsqEpQzU\npNCYM3aP1V/fxxVdT4DWvSI53JHTwHQamKdMWtEXetWVbP5zSROniYKFXd/xrD9X\n0jiGHey3lEtylXRIPxe5s+wXoCmNLcJVnvTcDtwx/ne2NLHxp76lyc25At+6RgE6\nADjLVuoD7M4IFDkAsd8UQ8zM0Dww9SylIk/wgV3ZkifecvgUQRagrNUdUjR56EBZ\nraQrev4hhzOgwelT0kXCu3snbUuNY/lU53CoTzfBJ5UfEJ5pMw1ij6X0r5S9IVsy\nKLWH1hiO0NzU2c8ViUYCly4Fe9xMTFc6u2dy/dxf6FwERfGzETQxqZvSfrRX+GLj\n/QZAXiPg5178hT/m0Y3z5IGenIC/80Z9NCi+byF1WuJlzKjDcF/TU72zk0+PNM/H\nKuppf3JT4DyjiVzNC5YoWJT2QRMS9KLP5iKCSThwVceEEg5HfhQBRT9M6KIcFLSs\nmFjx9kNEEmc1E8hl5IR3+3Ry8G5/bTIIruz14jgeY9u5jhL8Vyyvo41jgt9sLHR1\n/J1TxKfkgksYev7PoX6/ZzJ1ksWKZY5NFoDXTNYUgzFUTOoEaOg3BAQKadb3Qbbq\nXIrxmPBdgrn9QI7NCgfnAY3Tb4EEjs3ON/BNyEhUENcXOH6I1NbcuBQ7g9P73kE4\nVORdoc8MdJ5eoKBpO8Ww8HECAwEAAQ== +616ae350:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyduVzi1mWm+lYo2Tqt/0\nXkCIWrDNP1QBMVPrE0/ZlU2bCGSoo2Z9FHQKz/mTyMRlhNqTfhJ5qU3U9XlyGOPJ\npiM+b91g26pnpXJ2Q2kOypSgOMOPA4cQ42PkHBEqhuzssfj9t7x47ppS94bboh46\nxLSDRff/NAbtwTpvhStV3URYkxFG++cKGGa5MPXBrxIp+iZf9GnuxVdST5PGiVGP\nODL/b69sPJQNbJHVquqUTOh5Ry8uuD2WZuXfKf7/C0jC/ie9m2+0CttNu9tMciGM\nEyKG1/Xhk5iIWO43m4SrrT2WkFlcZ1z2JSf9Pjm4C2+HovYpihwwdM/OdP8Xmsnr\nDzVB4YvQiW+IHBjStHVuyiZWc+JsgEPJzisNY0Wyc/kNyNtqVKpX6dRhMLanLmy+\nf53cCSI05KPQAcGj6tdL+D60uKDkt+FsDa0BTAobZ31OsFVid0vCXtsbplNhW1IF\nHwsGXBTVcfXg44RLyL8Lk/2dQxDHNHzAUslJXzPxaHBLmt++2COa2EI1iWlvtznk\nOk9WP8SOAIj+xdqoiHcC4j72BOVVgiITIJNHrbppZCq6qPR+fgXmXa+sDcGh30m6\n9Wpbr28kLMSHiENCWTdsFij+NQTd5S47H7XTROHnalYDuF1RpS+DpQidT5tUimaT\nJZDr++FjKrnnijbyNF8b98UCAwEAAQ== +616db30d:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnpUpyWDWjlUk3smlWeA0\nlIMW+oJ38t92CRLHH3IqRhyECBRW0d0aRGtq7TY8PmxjjvBZrxTNDpJT6KUk4LRm\na6A6IuAI7QnNK8SJqM0DLzlpygd7GJf8ZL9SoHSH+gFsYF67Cpooz/YDqWrlN7Vw\ntO00s0B+eXy+PCXYU7VSfuWFGK8TGEv6HfGMALLjhqMManyvfp8hz3ubN1rK3c8C\nUS/ilRh1qckdbtPvoDPhSbTDmfU1g/EfRSIEXBrIMLg9ka/XB9PvWRrekrppnQzP\nhP9YE3x/wbFc5QqQWiRCYyQl/rgIMOXvIxhkfe8H5n1Et4VAorkpEAXdsfN8KSVv\nLSMazVlLp9GYq5SUpqYX3KnxdWBgN7BJoZ4sltsTpHQ/34SXWfu3UmyUveWj7wp0\nx9hwsPirVI00EEea9AbP7NM2rAyu6ukcm4m6ATd2DZJIViq2es6m60AE6SMCmrQF\nwmk4H/kdQgeAELVfGOm2VyJ3z69fQuywz7xu27S6zTKi05Qlnohxol4wVb6OB7qG\nLPRtK9ObgzRo/OPumyXqlzAi/Yvyd1ZQk8labZps3e16bQp8+pVPiumWioMFJDWV\nGZjCmyMSU8V6MB6njbgLHoyg2LCukCAeSjbPGGGYhnKLm1AKSoJh3IpZuqcKCk5C\n8CM1S15HxV78s9dFntEqIokCAwEAAQ== +' +__Keyring= +__SkipSigCheck=0 +__UseMirror=0 + +__UnprocessedBuildArgs= +while :; do + if [[ "$#" -le 0 ]]; then + break + fi + + lowerI="$(echo "$1" | tr "[:upper:]" "[:lower:]")" + case $lowerI in + -\?|-h|--help) + usage + ;; + arm) + __BuildArch=arm + __UbuntuArch=armhf + __AlpineArch=armv7 + __QEMUArch=arm + ;; + arm64) + __BuildArch=arm64 + __UbuntuArch=arm64 + __AlpineArch=aarch64 + __QEMUArch=aarch64 + __FreeBSDArch=arm64 + __FreeBSDMachineArch=aarch64 + ;; + armel) + __BuildArch=armel + __UbuntuArch=armel + __UbuntuRepo="http://ftp.debian.org/debian/" + __CodeName=jessie + ;; + armv6) + __BuildArch=armv6 + __UbuntuArch=armhf + __QEMUArch=arm + __UbuntuRepo="http://raspbian.raspberrypi.org/raspbian/" + __CodeName=buster + __LLDB_Package="liblldb-6.0-dev" + + if [[ -e "/usr/share/keyrings/raspbian-archive-keyring.gpg" ]]; then + __Keyring="--keyring /usr/share/keyrings/raspbian-archive-keyring.gpg" + fi + ;; + riscv64) + __BuildArch=riscv64 + __AlpineArch=riscv64 + __AlpinePackages="${__AlpinePackages// lldb-dev/}" + __QEMUArch=riscv64 + __UbuntuArch=riscv64 + __UbuntuRepo="http://deb.debian.org/debian" + __UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}" + unset __LLDB_Package + + if [[ -e "/usr/share/keyrings/debian-archive-keyring.gpg" ]]; then + __Keyring="--keyring /usr/share/keyrings/debian-archive-keyring.gpg --include=debian-archive-keyring" + fi + ;; + ppc64le) + __BuildArch=ppc64le + __AlpineArch=ppc64le + __QEMUArch=ppc64le + __UbuntuArch=ppc64el + __UbuntuRepo="http://ports.ubuntu.com/ubuntu-ports/" + __UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}" + __UbuntuPackages="${__UbuntuPackages// libomp-dev/}" + __UbuntuPackages="${__UbuntuPackages// libomp5/}" + unset __LLDB_Package + ;; + s390x) + __BuildArch=s390x + __AlpineArch=s390x + __QEMUArch=s390x + __UbuntuArch=s390x + __UbuntuRepo="http://ports.ubuntu.com/ubuntu-ports/" + __UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}" + __UbuntuPackages="${__UbuntuPackages// libomp-dev/}" + __UbuntuPackages="${__UbuntuPackages// libomp5/}" + unset __LLDB_Package + ;; + x64) + __BuildArch=x64 + __AlpineArch=x86_64 + __UbuntuArch=amd64 + __FreeBSDArch=amd64 + __FreeBSDMachineArch=amd64 + __illumosArch=x86_64 + __HaikuArch=x86_64 + __UbuntuRepo="http://archive.ubuntu.com/ubuntu/" + ;; + x86) + __BuildArch=x86 + __UbuntuArch=i386 + __AlpineArch=x86 + __UbuntuRepo="http://archive.ubuntu.com/ubuntu/" + ;; + lldb*) + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + majorVersion="${version%%.*}" + + [ -z "${version##*.*}" ] && minorVersion="${version#*.}" + if [ -z "$minorVersion" ]; then + minorVersion=0 + fi + + # for versions > 6.0, lldb has dropped the minor version + if [ "$majorVersion" -le 6 ]; then + version="$majorVersion.$minorVersion" + else + version="$majorVersion" + fi + + __LLDB_Package="liblldb-${version}-dev" + ;; + no-lldb) + unset __LLDB_Package + ;; + llvm*) + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + __LLVM_MajorVersion="${version%%.*}" + + [ -z "${version##*.*}" ] && __LLVM_MinorVersion="${version#*.}" + if [ -z "$__LLVM_MinorVersion" ]; then + __LLVM_MinorVersion=0 + fi + + # for versions > 6.0, lldb has dropped the minor version + if [ "$__LLVM_MajorVersion" -gt 6 ]; then + __LLVM_MinorVersion= + fi + + ;; + xenial) # Ubuntu 16.04 + if [[ "$__CodeName" != "jessie" ]]; then + __CodeName=xenial + fi + ;; + zesty) # Ubuntu 17.04 + if [[ "$__CodeName" != "jessie" ]]; then + __CodeName=zesty + fi + ;; + bionic) # Ubuntu 18.04 + if [[ "$__CodeName" != "jessie" ]]; then + __CodeName=bionic + fi + ;; + focal) # Ubuntu 20.04 + if [[ "$__CodeName" != "jessie" ]]; then + __CodeName=focal + fi + ;; + jammy) # Ubuntu 22.04 + if [[ "$__CodeName" != "jessie" ]]; then + __CodeName=jammy + fi + ;; + jessie) # Debian 8 + __CodeName=jessie + + if [[ -z "$__UbuntuRepo" ]]; then + __UbuntuRepo="http://ftp.debian.org/debian/" + fi + ;; + stretch) # Debian 9 + __CodeName=stretch + __LLDB_Package="liblldb-6.0-dev" + + if [[ -z "$__UbuntuRepo" ]]; then + __UbuntuRepo="http://ftp.debian.org/debian/" + fi + ;; + buster) # Debian 10 + __CodeName=buster + __LLDB_Package="liblldb-6.0-dev" + + if [[ -z "$__UbuntuRepo" ]]; then + __UbuntuRepo="http://ftp.debian.org/debian/" + fi + ;; + bullseye) # Debian 11 + __CodeName=bullseye + + if [[ -z "$__UbuntuRepo" ]]; then + __UbuntuRepo="http://ftp.debian.org/debian/" + fi + ;; + sid) # Debian sid + __CodeName=sid + + if [[ -z "$__UbuntuRepo" ]]; then + __UbuntuRepo="http://ftp.debian.org/debian/" + fi + ;; + tizen) + __CodeName= + __UbuntuRepo= + __Tizen=tizen + ;; + alpine*) + __CodeName=alpine + __UbuntuRepo= + + if [[ "$lowerI" == "alpineedge" ]]; then + __AlpineVersion=edge + else + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + __AlpineMajorVersion="${version%%.*}" + __AlpineMinorVersion="${version#*.}" + __AlpineVersion="$__AlpineMajorVersion.$__AlpineMinorVersion" + fi + ;; + freebsd13) + __CodeName=freebsd + __SkipUnmount=1 + ;; + freebsd14) + __CodeName=freebsd + __FreeBSDBase="14.0-RELEASE" + __FreeBSDABI="14" + __SkipUnmount=1 + ;; + illumos) + __CodeName=illumos + __SkipUnmount=1 + ;; + haiku) + __CodeName=haiku + __SkipUnmount=1 + ;; + --skipunmount) + __SkipUnmount=1 + ;; + --skipsigcheck) + __SkipSigCheck=1 + ;; + --rootfsdir|-rootfsdir) + shift + __RootfsDir="$1" + ;; + --use-mirror) + __UseMirror=1 + ;; + --use-jobs) + shift + MAXJOBS=$1 + ;; + *) + __UnprocessedBuildArgs="$__UnprocessedBuildArgs $1" + ;; + esac + + shift +done + +case "$__AlpineVersion" in + 3.14) __AlpinePackages+=" llvm11-libs" ;; + 3.15) __AlpinePackages+=" llvm12-libs" ;; + 3.16) __AlpinePackages+=" llvm13-libs" ;; + 3.17) __AlpinePackages+=" llvm15-libs" ;; + edge) __AlpineLlvmLibsLookup=1 ;; + *) + if [[ "$__AlpineArch" =~ s390x|ppc64le ]]; then + __AlpineVersion=3.15 # minimum version that supports lldb-dev + __AlpinePackages+=" llvm12-libs" + elif [[ "$__AlpineArch" == "x86" ]]; then + __AlpineVersion=3.17 # minimum version that supports lldb-dev + __AlpinePackages+=" llvm15-libs" + elif [[ "$__AlpineArch" == "riscv64" ]]; then + __AlpineLlvmLibsLookup=1 + __AlpineVersion=edge # minimum version with APKINDEX.tar.gz (packages archive) + else + __AlpineVersion=3.13 # 3.13 to maximize compatibility + __AlpinePackages+=" llvm10-libs" + + if [[ "$__AlpineArch" == "armv7" ]]; then + __AlpinePackages="${__AlpinePackages//numactl-dev/}" + fi + fi +esac + +if [[ "$__AlpineVersion" =~ 3\.1[345] ]]; then + # compiler-rt--static was merged in compiler-rt package in alpine 3.16 + # for older versions, we need compiler-rt--static, so replace the name + __AlpinePackages="${__AlpinePackages/compiler-rt/compiler-rt-static}" +fi + +if [[ "$__BuildArch" == "armel" ]]; then + __LLDB_Package="lldb-3.5-dev" +fi + +if [[ "$__CodeName" == "xenial" && "$__UbuntuArch" == "armhf" ]]; then + # libnuma-dev is not available on armhf for xenial + __UbuntuPackages="${__UbuntuPackages//libnuma-dev/}" +fi + +__UbuntuPackages+=" ${__LLDB_Package:-}" + +if [[ -n "$__LLVM_MajorVersion" ]]; then + __UbuntuPackages+=" libclang-common-${__LLVM_MajorVersion}${__LLVM_MinorVersion:+.$__LLVM_MinorVersion}-dev" +fi + +if [[ -z "$__RootfsDir" && -n "$ROOTFS_DIR" ]]; then + __RootfsDir="$ROOTFS_DIR" +fi + +if [[ -z "$__RootfsDir" ]]; then + __RootfsDir="$__CrossDir/../../../.tools/rootfs/$__BuildArch" +fi + +if [[ -d "$__RootfsDir" ]]; then + if [[ "$__SkipUnmount" == "0" ]]; then + umount "$__RootfsDir"/* || true + fi + rm -rf "$__RootfsDir" +fi + +mkdir -p "$__RootfsDir" +__RootfsDir="$( cd "$__RootfsDir" && pwd )" + +__hasWget= +ensureDownloadTool() +{ + if command -v wget &> /dev/null; then + __hasWget=1 + elif command -v curl &> /dev/null; then + __hasWget=0 + else + >&2 echo "ERROR: either wget or curl is required by this script." + exit 1 + fi +} + +if [[ "$__CodeName" == "alpine" ]]; then + __ApkToolsVersion=2.12.11 + __ApkToolsDir="$(mktemp -d)" + __ApkKeysDir="$(mktemp -d)" + arch="$(uname -m)" + + ensureDownloadTool + + if [[ "$__hasWget" == 1 ]]; then + wget -P "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static" + else + curl -SLO --create-dirs --output-dir "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static" + fi + if [[ "$arch" == "x86_64" ]]; then + __ApkToolsSHA512SUM="53e57b49230da07ef44ee0765b9592580308c407a8d4da7125550957bb72cb59638e04f8892a18b584451c8d841d1c7cb0f0ab680cc323a3015776affaa3be33" + elif [[ "$arch" == "aarch64" ]]; then + __ApkToolsSHA512SUM="9e2b37ecb2b56c05dad23d379be84fd494c14bd730b620d0d576bda760588e1f2f59a7fcb2f2080577e0085f23a0ca8eadd993b4e61c2ab29549fdb71969afd0" + else + echo "WARNING: add missing hash for your host architecture. To find the value, use: 'find /tmp -name apk.static -exec sha512sum {} \;'" + fi + echo "$__ApkToolsSHA512SUM $__ApkToolsDir/apk.static" | sha512sum -c + chmod +x "$__ApkToolsDir/apk.static" + + if [[ -f "/usr/bin/qemu-$__QEMUArch-static" ]]; then + mkdir -p "$__RootfsDir"/usr/bin + cp -v "/usr/bin/qemu-$__QEMUArch-static" "$__RootfsDir/usr/bin" + fi + + if [[ "$__AlpineVersion" == "edge" ]]; then + version=edge + else + version="v$__AlpineVersion" + fi + + for line in $__AlpineKeys; do + id="${line%%:*}" + content="${line#*:}" + + echo -e "-----BEGIN PUBLIC KEY-----\n$content\n-----END PUBLIC KEY-----" > "$__ApkKeysDir/alpine-devel@lists.alpinelinux.org-$id.rsa.pub" + done + + if [[ "$__SkipSigCheck" == "1" ]]; then + __ApkSignatureArg="--allow-untrusted" + else + __ApkSignatureArg="--keys-dir $__ApkKeysDir" + fi + + # initialize DB + # shellcheck disable=SC2086 + "$__ApkToolsDir/apk.static" \ + -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ + -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ + -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" --initdb add + + if [[ "$__AlpineLlvmLibsLookup" == 1 ]]; then + # shellcheck disable=SC2086 + __AlpinePackages+=" $("$__ApkToolsDir/apk.static" \ + -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ + -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ + -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" \ + search 'llvm*-libs' | grep -E '^llvm' | sort | tail -1 | sed 's/-[^-]*//2g')" + fi + + # install all packages in one go + # shellcheck disable=SC2086 + "$__ApkToolsDir/apk.static" \ + -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ + -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ + -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" \ + add $__AlpinePackages + + rm -r "$__ApkToolsDir" +elif [[ "$__CodeName" == "freebsd" ]]; then + mkdir -p "$__RootfsDir"/usr/local/etc + JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} + + ensureDownloadTool + + if [[ "$__hasWget" == 1 ]]; then + wget -O- "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + else + curl -SL "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + fi + echo "ABI = \"FreeBSD:${__FreeBSDABI}:${__FreeBSDMachineArch}\"; FINGERPRINTS = \"${__RootfsDir}/usr/share/keys\"; REPOS_DIR = [\"${__RootfsDir}/etc/pkg\"]; REPO_AUTOUPDATE = NO; RUN_SCRIPTS = NO;" > "${__RootfsDir}"/usr/local/etc/pkg.conf + echo "FreeBSD: { url: \"pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"${__RootfsDir}/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf + mkdir -p "$__RootfsDir"/tmp + # get and build package manager + if [[ "$__hasWget" == 1 ]]; then + wget -O- "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + else + curl -SL "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + fi + cd "$__RootfsDir/tmp/pkg-${__FreeBSDPkg}" + # needed for install to succeed + mkdir -p "$__RootfsDir"/host/etc + ./autogen.sh && ./configure --prefix="$__RootfsDir"/host && make -j "$JOBS" && make install + rm -rf "$__RootfsDir/tmp/pkg-${__FreeBSDPkg}" + # install packages we need. + INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf update + # shellcheck disable=SC2086 + INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf install --yes $__FreeBSDPackages +elif [[ "$__CodeName" == "illumos" ]]; then + mkdir "$__RootfsDir/tmp" + pushd "$__RootfsDir/tmp" + JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} + + ensureDownloadTool + + echo "Downloading sysroot." + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + else + curl -SL https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + fi + echo "Building binutils. Please wait.." + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + else + curl -SL https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + fi + mkdir build-binutils && cd build-binutils + ../binutils-2.33.1/configure --prefix="$__RootfsDir" --target="${__illumosArch}-sun-solaris2.10" --program-prefix="${__illumosArch}-illumos-" --with-sysroot="$__RootfsDir" + make -j "$JOBS" && make install && cd .. + echo "Building gcc. Please wait.." + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + else + curl -SL https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + fi + CFLAGS="-fPIC" + CXXFLAGS="-fPIC" + CXXFLAGS_FOR_TARGET="-fPIC" + CFLAGS_FOR_TARGET="-fPIC" + export CFLAGS CXXFLAGS CXXFLAGS_FOR_TARGET CFLAGS_FOR_TARGET + mkdir build-gcc && cd build-gcc + ../gcc-8.4.0/configure --prefix="$__RootfsDir" --target="${__illumosArch}-sun-solaris2.10" --program-prefix="${__illumosArch}-illumos-" --with-sysroot="$__RootfsDir" --with-gnu-as \ + --with-gnu-ld --disable-nls --disable-libgomp --disable-libquadmath --disable-libssp --disable-libvtv --disable-libcilkrts --disable-libada --disable-libsanitizer \ + --disable-libquadmath-support --disable-shared --enable-tls + make -j "$JOBS" && make install && cd .. + BaseUrl=https://pkgsrc.smartos.org + if [[ "$__UseMirror" == 1 ]]; then + BaseUrl=https://pkgsrc.smartos.skylime.net + fi + BaseUrl="$BaseUrl/packages/SmartOS/trunk/${__illumosArch}/All" + echo "Downloading manifest" + if [[ "$__hasWget" == 1 ]]; then + wget "$BaseUrl" + else + curl -SLO "$BaseUrl" + fi + echo "Downloading dependencies." + read -ra array <<<"$__IllumosPackages" + for package in "${array[@]}"; do + echo "Installing '$package'" + # find last occurrence of package in listing and extract its name + package="$(sed -En '/.*href="('"$package"'-[0-9].*).tgz".*/h;$!d;g;s//\1/p' All)" + echo "Resolved name '$package'" + if [[ "$__hasWget" == 1 ]]; then + wget "$BaseUrl"/"$package".tgz + else + curl -SLO "$BaseUrl"/"$package".tgz + fi + ar -x "$package".tgz + tar --skip-old-files -xzf "$package".tmp.tg* -C "$__RootfsDir" 2>/dev/null + done + echo "Cleaning up temporary files." + popd + rm -rf "$__RootfsDir"/{tmp,+*} + mkdir -p "$__RootfsDir"/usr/include/net + mkdir -p "$__RootfsDir"/usr/include/netpacket + if [[ "$__hasWget" == 1 ]]; then + wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h + wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h + wget -P "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h + wget -P "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + else + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + fi +elif [[ "$__CodeName" == "haiku" ]]; then + JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} + + echo "Building Haiku sysroot for $__HaikuArch" + mkdir -p "$__RootfsDir/tmp" + pushd "$__RootfsDir/tmp" + + mkdir "$__RootfsDir/tmp/download" + + ensureDownloadTool + + echo "Downloading Haiku package tool" + git clone https://github.com/haiku/haiku-toolchains-ubuntu --depth 1 "$__RootfsDir/tmp/script" + if [[ "$__hasWget" == 1 ]]; then + wget -O "$__RootfsDir/tmp/download/hosttools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --hosttools)" + else + curl -SLo "$__RootfsDir/tmp/download/hosttools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --hosttools)" + fi + + unzip -o "$__RootfsDir/tmp/download/hosttools.zip" -d "$__RootfsDir/tmp/bin" + + DepotBaseUrl="https://depot.haiku-os.org/__api/v2/pkg/get-pkg" + HpkgBaseUrl="https://eu.hpkg.haiku-os.org/haiku/master/$__HaikuArch/current" + + # Download Haiku packages + echo "Downloading Haiku packages" + read -ra array <<<"$__HaikuPackages" + for package in "${array[@]}"; do + echo "Downloading $package..." + # API documented here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L60 + # The schema here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L598 + if [[ "$__hasWget" == 1 ]]; then + hpkgDownloadUrl="$(wget -qO- --post-data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ + --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" + wget -P "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + else + hpkgDownloadUrl="$(curl -sSL -XPOST --data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ + --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" + curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + fi + done + for package in haiku haiku_devel; do + echo "Downloading $package..." + if [[ "$__hasWget" == 1 ]]; then + hpkgVersion="$(wget -qO- "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" + wget -P "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + else + hpkgVersion="$(curl -sSL "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" + curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + fi + done + + # Set up the sysroot + echo "Setting up sysroot and extracting required packages" + mkdir -p "$__RootfsDir/boot/system" + for file in "$__RootfsDir/tmp/download/"*.hpkg; do + echo "Extracting $file..." + LD_LIBRARY_PATH="$__RootfsDir/tmp/bin" "$__RootfsDir/tmp/bin/package" extract -C "$__RootfsDir/boot/system" "$file" + done + + # Download buildtools + echo "Downloading Haiku buildtools" + if [[ "$__hasWget" == 1 ]]; then + wget -O "$__RootfsDir/tmp/download/buildtools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --buildtools --arch=$__HaikuArch)" + else + curl -SLo "$__RootfsDir/tmp/download/buildtools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --buildtools --arch=$__HaikuArch)" + fi + unzip -o "$__RootfsDir/tmp/download/buildtools.zip" -d "$__RootfsDir" + + # Cleaning up temporary files + echo "Cleaning up temporary files" + popd + rm -rf "$__RootfsDir/tmp" +elif [[ -n "$__CodeName" ]]; then + + if [[ "$__SkipSigCheck" == "0" ]]; then + __Keyring="$__Keyring --force-check-gpg" + fi + + # shellcheck disable=SC2086 + debootstrap "--variant=minbase" $__Keyring --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo" + cp "$__CrossDir/$__BuildArch/sources.list.$__CodeName" "$__RootfsDir/etc/apt/sources.list" + chroot "$__RootfsDir" apt-get update + chroot "$__RootfsDir" apt-get -f -y install + # shellcheck disable=SC2086 + chroot "$__RootfsDir" apt-get -y install $__UbuntuPackages + chroot "$__RootfsDir" symlinks -cr /usr + chroot "$__RootfsDir" apt-get clean + + if [[ "$__SkipUnmount" == "0" ]]; then + umount "$__RootfsDir"/* || true + fi + + if [[ "$__BuildArch" == "armel" && "$__CodeName" == "jessie" ]]; then + pushd "$__RootfsDir" + patch -p1 < "$__CrossDir/$__BuildArch/armel.jessie.patch" + popd + fi +elif [[ "$__Tizen" == "tizen" ]]; then + ROOTFS_DIR="$__RootfsDir" "$__CrossDir/tizen-build-rootfs.sh" "$__BuildArch" +else + echo "Unsupported target platform." + usage +fi diff --git a/eng/common/cross/ppc64le/sources.list.bionic b/eng/common/cross/ppc64le/sources.list.bionic new file mode 100644 index 000000000..210955740 --- /dev/null +++ b/eng/common/cross/ppc64le/sources.list.bionic @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse diff --git a/eng/common/cross/riscv64/sources.list.sid b/eng/common/cross/riscv64/sources.list.sid new file mode 100644 index 000000000..b5f7a7e6e --- /dev/null +++ b/eng/common/cross/riscv64/sources.list.sid @@ -0,0 +1 @@ +deb http://deb.debian.org/debian sid main diff --git a/eng/common/cross/riscv64/tizen/tizen.patch b/eng/common/cross/riscv64/tizen/tizen.patch new file mode 100644 index 000000000..eb6d1c074 --- /dev/null +++ b/eng/common/cross/riscv64/tizen/tizen.patch @@ -0,0 +1,9 @@ +diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so +--- a/usr/lib64/libc.so 2016-12-30 23:00:08.284951863 +0900 ++++ b/usr/lib64/libc.so 2016-12-30 23:00:32.140951815 +0900 +@@ -2,4 +2,4 @@ + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ + OUTPUT_FORMAT(elf64-littleriscv) +-GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-riscv64-lp64d.so.1 ) ) ++GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-riscv64-lp64d.so.1 ) ) diff --git a/eng/common/cross/s390x/sources.list.bionic b/eng/common/cross/s390x/sources.list.bionic new file mode 100644 index 000000000..210955740 --- /dev/null +++ b/eng/common/cross/s390x/sources.list.bionic @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse diff --git a/eng/common/cross/tizen-build-rootfs.sh b/eng/common/cross/tizen-build-rootfs.sh new file mode 100644 index 000000000..ba31c9328 --- /dev/null +++ b/eng/common/cross/tizen-build-rootfs.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +set -e + +ARCH=$1 +LINK_ARCH=$ARCH + +case "$ARCH" in + arm) + TIZEN_ARCH="armv7hl" + ;; + armel) + TIZEN_ARCH="armv7l" + LINK_ARCH="arm" + ;; + arm64) + TIZEN_ARCH="aarch64" + ;; + x86) + TIZEN_ARCH="i686" + ;; + x64) + TIZEN_ARCH="x86_64" + LINK_ARCH="x86" + ;; + riscv64) + TIZEN_ARCH="riscv64" + LINK_ARCH="riscv" + ;; + *) + echo "Unsupported architecture for tizen: $ARCH" + exit 1 +esac + +__CrossDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +__TIZEN_CROSSDIR="$__CrossDir/${ARCH}/tizen" + +if [[ -z "$ROOTFS_DIR" ]]; then + echo "ROOTFS_DIR is not defined." + exit 1; +fi + +TIZEN_TMP_DIR=$ROOTFS_DIR/tizen_tmp +mkdir -p $TIZEN_TMP_DIR + +# Download files +echo ">>Start downloading files" +VERBOSE=1 $__CrossDir/tizen-fetch.sh $TIZEN_TMP_DIR $TIZEN_ARCH +echo "<>Start constructing Tizen rootfs" +TIZEN_RPM_FILES=`ls $TIZEN_TMP_DIR/*.rpm` +cd $ROOTFS_DIR +for f in $TIZEN_RPM_FILES; do + rpm2cpio $f | cpio -idm --quiet +done +echo "<>Start configuring Tizen rootfs" +ln -sfn asm-${LINK_ARCH} ./usr/include/asm +patch -p1 < $__TIZEN_CROSSDIR/tizen.patch +if [[ "$TIZEN_ARCH" == "riscv64" ]]; then + echo "Fixing broken symlinks in $PWD" + rm ./usr/lib64/libresolv.so + ln -s ../../lib64/libresolv.so.2 ./usr/lib64/libresolv.so + rm ./usr/lib64/libpthread.so + ln -s ../../lib64/libpthread.so.0 ./usr/lib64/libpthread.so + rm ./usr/lib64/libdl.so + ln -s ../../lib64/libdl.so.2 ./usr/lib64/libdl.so + rm ./usr/lib64/libutil.so + ln -s ../../lib64/libutil.so.1 ./usr/lib64/libutil.so + rm ./usr/lib64/libm.so + ln -s ../../lib64/libm.so.6 ./usr/lib64/libm.so + rm ./usr/lib64/librt.so + ln -s ../../lib64/librt.so.1 ./usr/lib64/librt.so + rm ./lib/ld-linux-riscv64-lp64d.so.1 + ln -s ../lib64/ld-linux-riscv64-lp64d.so.1 ./lib/ld-linux-riscv64-lp64d.so.1 +fi +echo "</dev/null; then + VERBOSE=0 +fi + +Log() +{ + if [ $VERBOSE -ge $1 ]; then + echo ${@:2} + fi +} + +Inform() +{ + Log 1 -e "\x1B[0;34m$@\x1B[m" +} + +Debug() +{ + Log 2 -e "\x1B[0;32m$@\x1B[m" +} + +Error() +{ + >&2 Log 0 -e "\x1B[0;31m$@\x1B[m" +} + +Fetch() +{ + URL=$1 + FILE=$2 + PROGRESS=$3 + if [ $VERBOSE -ge 1 ] && [ $PROGRESS ]; then + CURL_OPT="--progress-bar" + else + CURL_OPT="--silent" + fi + curl $CURL_OPT $URL > $FILE +} + +hash curl 2> /dev/null || { Error "Require 'curl' Aborting."; exit 1; } +hash xmllint 2> /dev/null || { Error "Require 'xmllint' Aborting."; exit 1; } +hash sha256sum 2> /dev/null || { Error "Require 'sha256sum' Aborting."; exit 1; } + +TMPDIR=$1 +if [ ! -d $TMPDIR ]; then + TMPDIR=./tizen_tmp + Debug "Create temporary directory : $TMPDIR" + mkdir -p $TMPDIR +fi + +TIZEN_ARCH=$2 + +TIZEN_URL=http://download.tizen.org/snapshots/TIZEN/Tizen +BUILD_XML=build.xml +REPOMD_XML=repomd.xml +PRIMARY_XML=primary.xml +TARGET_URL="http://__not_initialized" + +Xpath_get() +{ + XPATH_RESULT='' + XPATH=$1 + XML_FILE=$2 + RESULT=$(xmllint --xpath $XPATH $XML_FILE) + if [[ -z ${RESULT// } ]]; then + Error "Can not find target from $XML_FILE" + Debug "Xpath = $XPATH" + exit 1 + fi + XPATH_RESULT=$RESULT +} + +fetch_tizen_pkgs_init() +{ + TARGET=$1 + PROFILE=$2 + Debug "Initialize TARGET=$TARGET, PROFILE=$PROFILE" + + TMP_PKG_DIR=$TMPDIR/tizen_${PROFILE}_pkgs + if [ -d $TMP_PKG_DIR ]; then rm -rf $TMP_PKG_DIR; fi + mkdir -p $TMP_PKG_DIR + + PKG_URL=$TIZEN_URL/$PROFILE/latest + + BUILD_XML_URL=$PKG_URL/$BUILD_XML + TMP_BUILD=$TMP_PKG_DIR/$BUILD_XML + TMP_REPOMD=$TMP_PKG_DIR/$REPOMD_XML + TMP_PRIMARY=$TMP_PKG_DIR/$PRIMARY_XML + TMP_PRIMARYGZ=${TMP_PRIMARY}.gz + + Fetch $BUILD_XML_URL $TMP_BUILD + + Debug "fetch $BUILD_XML_URL to $TMP_BUILD" + + TARGET_XPATH="//build/buildtargets/buildtarget[@name=\"$TARGET\"]/repo[@type=\"binary\"]/text()" + Xpath_get $TARGET_XPATH $TMP_BUILD + TARGET_PATH=$XPATH_RESULT + TARGET_URL=$PKG_URL/$TARGET_PATH + + REPOMD_URL=$TARGET_URL/repodata/repomd.xml + PRIMARY_XPATH='string(//*[local-name()="data"][@type="primary"]/*[local-name()="location"]/@href)' + + Fetch $REPOMD_URL $TMP_REPOMD + + Debug "fetch $REPOMD_URL to $TMP_REPOMD" + + Xpath_get $PRIMARY_XPATH $TMP_REPOMD + PRIMARY_XML_PATH=$XPATH_RESULT + PRIMARY_URL=$TARGET_URL/$PRIMARY_XML_PATH + + Fetch $PRIMARY_URL $TMP_PRIMARYGZ + + Debug "fetch $PRIMARY_URL to $TMP_PRIMARYGZ" + + gunzip $TMP_PRIMARYGZ + + Debug "unzip $TMP_PRIMARYGZ to $TMP_PRIMARY" +} + +fetch_tizen_pkgs() +{ + ARCH=$1 + PACKAGE_XPATH_TPL='string(//*[local-name()="metadata"]/*[local-name()="package"][*[local-name()="name"][text()="_PKG_"]][*[local-name()="arch"][text()="_ARCH_"]]/*[local-name()="location"]/@href)' + + PACKAGE_CHECKSUM_XPATH_TPL='string(//*[local-name()="metadata"]/*[local-name()="package"][*[local-name()="name"][text()="_PKG_"]][*[local-name()="arch"][text()="_ARCH_"]]/*[local-name()="checksum"]/text())' + + for pkg in ${@:2} + do + Inform "Fetching... $pkg" + XPATH=${PACKAGE_XPATH_TPL/_PKG_/$pkg} + XPATH=${XPATH/_ARCH_/$ARCH} + Xpath_get $XPATH $TMP_PRIMARY + PKG_PATH=$XPATH_RESULT + + XPATH=${PACKAGE_CHECKSUM_XPATH_TPL/_PKG_/$pkg} + XPATH=${XPATH/_ARCH_/$ARCH} + Xpath_get $XPATH $TMP_PRIMARY + CHECKSUM=$XPATH_RESULT + + PKG_URL=$TARGET_URL/$PKG_PATH + PKG_FILE=$(basename $PKG_PATH) + PKG_PATH=$TMPDIR/$PKG_FILE + + Debug "Download $PKG_URL to $PKG_PATH" + Fetch $PKG_URL $PKG_PATH true + + echo "$CHECKSUM $PKG_PATH" | sha256sum -c - > /dev/null + if [ $? -ne 0 ]; then + Error "Fail to fetch $PKG_URL to $PKG_PATH" + Debug "Checksum = $CHECKSUM" + exit 1 + fi + done +} + +if [ "$TIZEN_ARCH" == "riscv64" ]; then + BASE="Tizen-Base-RISCV" + UNIFIED="Tizen-Unified-RISCV" +else + BASE="Tizen-Base" + UNIFIED="Tizen-Unified" +fi + +Inform "Initialize ${TIZEN_ARCH} base" +fetch_tizen_pkgs_init standard $BASE +Inform "fetch common packages" +fetch_tizen_pkgs ${TIZEN_ARCH} gcc gcc-devel-static glibc glibc-devel libicu libicu-devel libatomic linux-glibc-devel keyutils keyutils-devel libkeyutils +Inform "fetch coreclr packages" +fetch_tizen_pkgs ${TIZEN_ARCH} libgcc libstdc++ libstdc++-devel libunwind libunwind-devel lttng-ust-devel lttng-ust userspace-rcu-devel userspace-rcu +if [ "$TIZEN_ARCH" != "riscv64" ]; then + fetch_tizen_pkgs ${TIZEN_ARCH} lldb lldb-devel +fi +Inform "fetch corefx packages" +fetch_tizen_pkgs ${TIZEN_ARCH} libcom_err libcom_err-devel zlib zlib-devel libopenssl11 libopenssl1.1-devel krb5 krb5-devel + +Inform "Initialize standard unified" +fetch_tizen_pkgs_init standard $UNIFIED +Inform "fetch corefx packages" +fetch_tizen_pkgs ${TIZEN_ARCH} gssdp gssdp-devel tizen-release + diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake new file mode 100644 index 000000000..9a4e285a5 --- /dev/null +++ b/eng/common/cross/toolchain.cmake @@ -0,0 +1,408 @@ +set(CROSS_ROOTFS $ENV{ROOTFS_DIR}) + +# reset platform variables (e.g. cmake 3.25 sets LINUX=1) +unset(LINUX) +unset(FREEBSD) +unset(ILLUMOS) +unset(ANDROID) +unset(TIZEN) +unset(HAIKU) + +set(TARGET_ARCH_NAME $ENV{TARGET_BUILD_ARCH}) +if(EXISTS ${CROSS_ROOTFS}/bin/freebsd-version) + set(CMAKE_SYSTEM_NAME FreeBSD) + set(FREEBSD 1) +elseif(EXISTS ${CROSS_ROOTFS}/usr/platform/i86pc) + set(CMAKE_SYSTEM_NAME SunOS) + set(ILLUMOS 1) +elseif(EXISTS ${CROSS_ROOTFS}/boot/system/develop/headers/config/HaikuConfig.h) + set(CMAKE_SYSTEM_NAME Haiku) + set(HAIKU 1) +else() + set(CMAKE_SYSTEM_NAME Linux) + set(LINUX 1) +endif() +set(CMAKE_SYSTEM_VERSION 1) + +if(EXISTS ${CROSS_ROOTFS}/etc/tizen-release) + set(TIZEN 1) +elseif(EXISTS ${CROSS_ROOTFS}/android_platform) + set(ANDROID 1) +endif() + +if(TARGET_ARCH_NAME STREQUAL "arm") + set(CMAKE_SYSTEM_PROCESSOR armv7l) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/armv7-alpine-linux-musleabihf) + set(TOOLCHAIN "armv7-alpine-linux-musleabihf") + elseif(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/armv6-alpine-linux-musleabihf) + set(TOOLCHAIN "armv6-alpine-linux-musleabihf") + else() + set(TOOLCHAIN "arm-linux-gnueabihf") + endif() + if(TIZEN) + set(TIZEN_TOOLCHAIN "armv7hl-tizen-linux-gnueabihf/9.2.0") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "arm64") + set(CMAKE_SYSTEM_PROCESSOR aarch64) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/aarch64-alpine-linux-musl) + set(TOOLCHAIN "aarch64-alpine-linux-musl") + elseif(LINUX) + set(TOOLCHAIN "aarch64-linux-gnu") + if(TIZEN) + set(TIZEN_TOOLCHAIN "aarch64-tizen-linux-gnu/9.2.0") + endif() + elseif(FREEBSD) + set(triple "aarch64-unknown-freebsd12") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "armel") + set(CMAKE_SYSTEM_PROCESSOR armv7l) + set(TOOLCHAIN "arm-linux-gnueabi") + if(TIZEN) + set(TIZEN_TOOLCHAIN "armv7l-tizen-linux-gnueabi/9.2.0") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "armv6") + set(CMAKE_SYSTEM_PROCESSOR armv6l) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/armv6-alpine-linux-musleabihf) + set(TOOLCHAIN "armv6-alpine-linux-musleabihf") + else() + set(TOOLCHAIN "arm-linux-gnueabihf") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "ppc64le") + set(CMAKE_SYSTEM_PROCESSOR ppc64le) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/powerpc64le-alpine-linux-musl) + set(TOOLCHAIN "powerpc64le-alpine-linux-musl") + else() + set(TOOLCHAIN "powerpc64le-linux-gnu") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "riscv64") + set(CMAKE_SYSTEM_PROCESSOR riscv64) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/riscv64-alpine-linux-musl) + set(TOOLCHAIN "riscv64-alpine-linux-musl") + else() + set(TOOLCHAIN "riscv64-linux-gnu") + if(TIZEN) + set(TIZEN_TOOLCHAIN "riscv64-tizen-linux-gnu/13.1.0") + endif() + endif() +elseif(TARGET_ARCH_NAME STREQUAL "s390x") + set(CMAKE_SYSTEM_PROCESSOR s390x) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/s390x-alpine-linux-musl) + set(TOOLCHAIN "s390x-alpine-linux-musl") + else() + set(TOOLCHAIN "s390x-linux-gnu") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "x64") + set(CMAKE_SYSTEM_PROCESSOR x86_64) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/x86_64-alpine-linux-musl) + set(TOOLCHAIN "x86_64-alpine-linux-musl") + elseif(LINUX) + set(TOOLCHAIN "x86_64-linux-gnu") + if(TIZEN) + set(TIZEN_TOOLCHAIN "x86_64-tizen-linux-gnu/9.2.0") + endif() + elseif(FREEBSD) + set(triple "x86_64-unknown-freebsd12") + elseif(ILLUMOS) + set(TOOLCHAIN "x86_64-illumos") + elseif(HAIKU) + set(TOOLCHAIN "x86_64-unknown-haiku") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "x86") + set(CMAKE_SYSTEM_PROCESSOR i686) + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/i586-alpine-linux-musl) + set(TOOLCHAIN "i586-alpine-linux-musl") + else() + set(TOOLCHAIN "i686-linux-gnu") + endif() + if(TIZEN) + set(TIZEN_TOOLCHAIN "i586-tizen-linux-gnu/9.2.0") + endif() +else() + message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only arm, arm64, armel, armv6, ppc64le, riscv64, s390x, x64 and x86 are supported!") +endif() + +if(DEFINED ENV{TOOLCHAIN}) + set(TOOLCHAIN $ENV{TOOLCHAIN}) +endif() + +# Specify include paths +if(TIZEN) + if(TARGET_ARCH_NAME STREQUAL "arm") + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/) + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/armv7hl-tizen-linux-gnueabihf) + endif() + if(TARGET_ARCH_NAME STREQUAL "armel") + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/) + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/armv7l-tizen-linux-gnueabi) + endif() + if(TARGET_ARCH_NAME STREQUAL "arm64") + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}/include/c++/) + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}/include/c++/aarch64-tizen-linux-gnu) + endif() + if(TARGET_ARCH_NAME STREQUAL "x86") + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/) + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/i586-tizen-linux-gnu) + endif() + if(TARGET_ARCH_NAME STREQUAL "x64") + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}/include/c++/) + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}/include/c++/x86_64-tizen-linux-gnu) + endif() + if(TARGET_ARCH_NAME STREQUAL "riscv64") + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}/include/c++/) + include_directories(SYSTEM ${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}/include/c++/riscv64-tizen-linux-gnu) + endif() +endif() + +if(ANDROID) + if(TARGET_ARCH_NAME STREQUAL "arm") + set(ANDROID_ABI armeabi-v7a) + elseif(TARGET_ARCH_NAME STREQUAL "arm64") + set(ANDROID_ABI arm64-v8a) + endif() + + # extract platform number required by the NDK's toolchain + file(READ "${CROSS_ROOTFS}/android_platform" RID_FILE_CONTENTS) + string(REPLACE "RID=" "" ANDROID_RID "${RID_FILE_CONTENTS}") + string(REGEX REPLACE ".*\\.([0-9]+)-.*" "\\1" ANDROID_PLATFORM "${ANDROID_RID}") + + set(ANDROID_TOOLCHAIN clang) + set(FEATURE_EVENT_TRACE 0) # disable event trace as there is no lttng-ust package in termux repository + set(CMAKE_SYSTEM_LIBRARY_PATH "${CROSS_ROOTFS}/usr/lib") + set(CMAKE_SYSTEM_INCLUDE_PATH "${CROSS_ROOTFS}/usr/include") + + # include official NDK toolchain script + include(${CROSS_ROOTFS}/../build/cmake/android.toolchain.cmake) +elseif(FREEBSD) + # we cross-compile by instructing clang + set(CMAKE_C_COMPILER_TARGET ${triple}) + set(CMAKE_CXX_COMPILER_TARGET ${triple}) + set(CMAKE_ASM_COMPILER_TARGET ${triple}) + set(CMAKE_SYSROOT "${CROSS_ROOTFS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -fuse-ld=lld") +elseif(ILLUMOS) + set(CMAKE_SYSROOT "${CROSS_ROOTFS}") + + include_directories(SYSTEM ${CROSS_ROOTFS}/include) + + set(TOOLSET_PREFIX ${TOOLCHAIN}-) + function(locate_toolchain_exec exec var) + string(TOUPPER ${exec} EXEC_UPPERCASE) + if(NOT "$ENV{CLR_${EXEC_UPPERCASE}}" STREQUAL "") + set(${var} "$ENV{CLR_${EXEC_UPPERCASE}}" PARENT_SCOPE) + return() + endif() + + find_program(EXEC_LOCATION_${exec} + NAMES + "${TOOLSET_PREFIX}${exec}${CLR_CMAKE_COMPILER_FILE_NAME_VERSION}" + "${TOOLSET_PREFIX}${exec}") + + if (EXEC_LOCATION_${exec} STREQUAL "EXEC_LOCATION_${exec}-NOTFOUND") + message(FATAL_ERROR "Unable to find toolchain executable. Name: ${exec}, Prefix: ${TOOLSET_PREFIX}.") + endif() + set(${var} ${EXEC_LOCATION_${exec}} PARENT_SCOPE) + endfunction() + + set(CMAKE_SYSTEM_PREFIX_PATH "${CROSS_ROOTFS}") + + locate_toolchain_exec(gcc CMAKE_C_COMPILER) + locate_toolchain_exec(g++ CMAKE_CXX_COMPILER) + + set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lssp") + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lssp") +elseif(HAIKU) + set(CMAKE_SYSROOT "${CROSS_ROOTFS}") + set(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};${CROSS_ROOTFS}/cross-tools-x86_64/bin") + + set(TOOLSET_PREFIX ${TOOLCHAIN}-) + function(locate_toolchain_exec exec var) + string(TOUPPER ${exec} EXEC_UPPERCASE) + if(NOT "$ENV{CLR_${EXEC_UPPERCASE}}" STREQUAL "") + set(${var} "$ENV{CLR_${EXEC_UPPERCASE}}" PARENT_SCOPE) + return() + endif() + + find_program(EXEC_LOCATION_${exec} + NAMES + "${TOOLSET_PREFIX}${exec}${CLR_CMAKE_COMPILER_FILE_NAME_VERSION}" + "${TOOLSET_PREFIX}${exec}") + + if (EXEC_LOCATION_${exec} STREQUAL "EXEC_LOCATION_${exec}-NOTFOUND") + message(FATAL_ERROR "Unable to find toolchain executable. Name: ${exec}, Prefix: ${TOOLSET_PREFIX}.") + endif() + set(${var} ${EXEC_LOCATION_${exec}} PARENT_SCOPE) + endfunction() + + set(CMAKE_SYSTEM_PREFIX_PATH "${CROSS_ROOTFS}") + + locate_toolchain_exec(gcc CMAKE_C_COMPILER) + locate_toolchain_exec(g++ CMAKE_CXX_COMPILER) + + set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lssp") + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lssp") + + # let CMake set up the correct search paths + include(Platform/Haiku) +else() + set(CMAKE_SYSROOT "${CROSS_ROOTFS}") + + set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") + set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") + set(CMAKE_ASM_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") +endif() + +# Specify link flags + +function(add_toolchain_linker_flag Flag) + set(Config "${ARGV1}") + set(CONFIG_SUFFIX "") + if (NOT Config STREQUAL "") + set(CONFIG_SUFFIX "_${Config}") + endif() + set("CMAKE_EXE_LINKER_FLAGS${CONFIG_SUFFIX}_INIT" "${CMAKE_EXE_LINKER_FLAGS${CONFIG_SUFFIX}_INIT} ${Flag}" PARENT_SCOPE) + set("CMAKE_SHARED_LINKER_FLAGS${CONFIG_SUFFIX}_INIT" "${CMAKE_SHARED_LINKER_FLAGS${CONFIG_SUFFIX}_INIT} ${Flag}" PARENT_SCOPE) +endfunction() + +if(LINUX) + add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/lib/${TOOLCHAIN}") + add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib/${TOOLCHAIN}") +endif() + +if(TARGET_ARCH_NAME MATCHES "^(arm|armel)$") + if(TIZEN) + add_toolchain_linker_flag("-B${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/lib") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") + endif() +elseif(TARGET_ARCH_NAME MATCHES "^(arm64|x64|riscv64)$") + if(TIZEN) + add_toolchain_linker_flag("-B${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/lib64") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib64") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}") + + add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/lib64") + add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64") + add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}") + endif() +elseif(TARGET_ARCH_NAME STREQUAL "s390x") + add_toolchain_linker_flag("--target=${TOOLCHAIN}") +elseif(TARGET_ARCH_NAME STREQUAL "x86") + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/i586-alpine-linux-musl) + add_toolchain_linker_flag("--target=${TOOLCHAIN}") + add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib/gcc/${TOOLCHAIN}") + endif() + add_toolchain_linker_flag(-m32) + if(TIZEN) + add_toolchain_linker_flag("-B${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/lib") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") + endif() +elseif(ILLUMOS) + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/lib/amd64") + add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/amd64/lib") +elseif(HAIKU) + add_toolchain_linker_flag("-lnetwork") + add_toolchain_linker_flag("-lroot") +endif() + +# Specify compile options + +if((TARGET_ARCH_NAME MATCHES "^(arm|arm64|armel|armv6|ppc64le|riscv64|s390x|x64|x86)$" AND NOT ANDROID AND NOT FREEBSD) OR ILLUMOS OR HAIKU) + set(CMAKE_C_COMPILER_TARGET ${TOOLCHAIN}) + set(CMAKE_CXX_COMPILER_TARGET ${TOOLCHAIN}) + set(CMAKE_ASM_COMPILER_TARGET ${TOOLCHAIN}) +endif() + +if(TARGET_ARCH_NAME MATCHES "^(arm|armel)$") + add_compile_options(-mthumb) + if (NOT DEFINED CLR_ARM_FPU_TYPE) + set (CLR_ARM_FPU_TYPE vfpv3) + endif (NOT DEFINED CLR_ARM_FPU_TYPE) + + add_compile_options (-mfpu=${CLR_ARM_FPU_TYPE}) + if (NOT DEFINED CLR_ARM_FPU_CAPABILITY) + set (CLR_ARM_FPU_CAPABILITY 0x7) + endif (NOT DEFINED CLR_ARM_FPU_CAPABILITY) + + add_definitions (-DCLR_ARM_FPU_CAPABILITY=${CLR_ARM_FPU_CAPABILITY}) + + # persist variables across multiple try_compile passes + list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CLR_ARM_FPU_TYPE CLR_ARM_FPU_CAPABILITY) + + if(TARGET_ARCH_NAME STREQUAL "armel") + add_compile_options(-mfloat-abi=softfp) + endif() +elseif(TARGET_ARCH_NAME STREQUAL "s390x") + add_compile_options("--target=${TOOLCHAIN}") +elseif(TARGET_ARCH_NAME STREQUAL "x86") + if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/i586-alpine-linux-musl) + add_compile_options(--target=${TOOLCHAIN}) + endif() + add_compile_options(-m32) + add_compile_options(-Wno-error=unused-command-line-argument) +endif() + +if(TIZEN) + if(TARGET_ARCH_NAME MATCHES "^(arm|armel|arm64|x86)$") + add_compile_options(-Wno-deprecated-declarations) # compile-time option + add_compile_options(-D__extern_always_inline=inline) # compile-time option + endif() +endif() + +# Set LLDB include and library paths for builds that need lldb. +if(TARGET_ARCH_NAME MATCHES "^(arm|armel|x86)$") + if(TARGET_ARCH_NAME STREQUAL "x86") + set(LLVM_CROSS_DIR "$ENV{LLVM_CROSS_HOME}") + else() # arm/armel case + set(LLVM_CROSS_DIR "$ENV{LLVM_ARM_HOME}") + endif() + if(LLVM_CROSS_DIR) + set(WITH_LLDB_LIBS "${LLVM_CROSS_DIR}/lib/" CACHE STRING "") + set(WITH_LLDB_INCLUDES "${LLVM_CROSS_DIR}/include" CACHE STRING "") + set(LLDB_H "${WITH_LLDB_INCLUDES}" CACHE STRING "") + set(LLDB "${LLVM_CROSS_DIR}/lib/liblldb.so" CACHE STRING "") + else() + if(TARGET_ARCH_NAME STREQUAL "x86") + set(WITH_LLDB_LIBS "${CROSS_ROOTFS}/usr/lib/i386-linux-gnu" CACHE STRING "") + set(CHECK_LLVM_DIR "${CROSS_ROOTFS}/usr/lib/llvm-3.8/include") + if(EXISTS "${CHECK_LLVM_DIR}" AND IS_DIRECTORY "${CHECK_LLVM_DIR}") + set(WITH_LLDB_INCLUDES "${CHECK_LLVM_DIR}") + else() + set(WITH_LLDB_INCLUDES "${CROSS_ROOTFS}/usr/lib/llvm-3.6/include") + endif() + else() # arm/armel case + set(WITH_LLDB_LIBS "${CROSS_ROOTFS}/usr/lib/${TOOLCHAIN}" CACHE STRING "") + set(WITH_LLDB_INCLUDES "${CROSS_ROOTFS}/usr/lib/llvm-3.6/include" CACHE STRING "") + endif() + endif() +endif() + +# Set C++ standard library options if specified +set(CLR_CMAKE_CXX_STANDARD_LIBRARY "" CACHE STRING "Standard library flavor to link against. Only supported with the Clang compiler.") +if (CLR_CMAKE_CXX_STANDARD_LIBRARY) + add_compile_options($<$:--stdlib=${CLR_CMAKE_CXX_STANDARD_LIBRARY}>) + add_link_options($<$:--stdlib=${CLR_CMAKE_CXX_STANDARD_LIBRARY}>) +endif() + +option(CLR_CMAKE_CXX_STANDARD_LIBRARY_STATIC "Statically link against the C++ standard library" OFF) +if(CLR_CMAKE_CXX_STANDARD_LIBRARY_STATIC) + add_link_options($<$:-static-libstdc++>) +endif() + +set(CLR_CMAKE_CXX_ABI_LIBRARY "" CACHE STRING "C++ ABI implementation library to link against. Only supported with the Clang compiler.") +if (CLR_CMAKE_CXX_ABI_LIBRARY) + # The user may specify the ABI library with the 'lib' prefix, like 'libstdc++'. Strip the prefix here so the linker finds the right library. + string(REGEX REPLACE "^lib(.+)" "\\1" CLR_CMAKE_CXX_ABI_LIBRARY ${CLR_CMAKE_CXX_ABI_LIBRARY}) + # We need to specify this as a linker-backend option as Clang will filter this option out when linking to libc++. + add_link_options("LINKER:-l${CLR_CMAKE_CXX_ABI_LIBRARY}") +endif() + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/eng/common/cross/x86/sources.list.bionic b/eng/common/cross/x86/sources.list.bionic new file mode 100644 index 000000000..a71ccadcf --- /dev/null +++ b/eng/common/cross/x86/sources.list.bionic @@ -0,0 +1,11 @@ +deb http://archive.ubuntu.com/ubuntu/ bionic main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ bionic main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted +deb-src http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted + +deb http://archive.ubuntu.com/ubuntu/ bionic-security main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ bionic-security main restricted universe multiverse diff --git a/eng/common/cross/x86/sources.list.xenial b/eng/common/cross/x86/sources.list.xenial new file mode 100644 index 000000000..ad9c5a014 --- /dev/null +++ b/eng/common/cross/x86/sources.list.xenial @@ -0,0 +1,11 @@ +deb http://archive.ubuntu.com/ubuntu/ xenial main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ xenial-backports main restricted +deb-src http://archive.ubuntu.com/ubuntu/ xenial-backports main restricted + +deb http://archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse diff --git a/eng/common/darc-init.ps1 b/eng/common/darc-init.ps1 new file mode 100644 index 000000000..8fda30bdc --- /dev/null +++ b/eng/common/darc-init.ps1 @@ -0,0 +1,47 @@ +param ( + $darcVersion = $null, + $versionEndpoint = 'https://maestro.dot.net/api/assets/darc-version?api-version=2019-01-16', + $verbosity = 'minimal', + $toolpath = $null +) + +. $PSScriptRoot\tools.ps1 + +function InstallDarcCli ($darcVersion, $toolpath) { + $darcCliPackageName = 'microsoft.dotnet.darc' + + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + $toolList = & "$dotnet" tool list -g + + if ($toolList -like "*$darcCliPackageName*") { + & "$dotnet" tool uninstall $darcCliPackageName -g + } + + # If the user didn't explicitly specify the darc version, + # query the Maestro API for the correct version of darc to install. + if (-not $darcVersion) { + $darcVersion = $(Invoke-WebRequest -Uri $versionEndpoint -UseBasicParsing).Content + } + + $arcadeServicesSource = 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' + + Write-Host "Installing Darc CLI version $darcVersion..." + Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.' + if (-not $toolpath) { + Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity -g" + & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g + }else { + Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity --tool-path '$toolpath'" + & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath" + } +} + +try { + InstallDarcCli $darcVersion $toolpath +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'Darc' -Message $_ + ExitWithExitCode 1 +} \ No newline at end of file diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh new file mode 100755 index 000000000..c305ae6bd --- /dev/null +++ b/eng/common/darc-init.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" +darcVersion='' +versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2019-01-16' +verbosity='minimal' + +while [[ $# > 0 ]]; do + opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + --darcversion) + darcVersion=$2 + shift + ;; + --versionendpoint) + versionEndpoint=$2 + shift + ;; + --verbosity) + verbosity=$2 + shift + ;; + --toolpath) + toolpath=$2 + shift + ;; + *) + echo "Invalid argument: $1" + usage + exit 1 + ;; + esac + + shift +done + +# resolve $source until the file is no longer a symlink +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +. "$scriptroot/tools.sh" + +if [ -z "$darcVersion" ]; then + darcVersion=$(curl -X GET "$versionEndpoint" -H "accept: text/plain") +fi + +function InstallDarcCli { + local darc_cli_package_name="microsoft.dotnet.darc" + + InitializeDotNetCli true + local dotnet_root=$_InitializeDotNetCli + + if [ -z "$toolpath" ]; then + local tool_list=$($dotnet_root/dotnet tool list -g) + if [[ $tool_list = *$darc_cli_package_name* ]]; then + echo $($dotnet_root/dotnet tool uninstall $darc_cli_package_name -g) + fi + else + local tool_list=$($dotnet_root/dotnet tool list --tool-path "$toolpath") + if [[ $tool_list = *$darc_cli_package_name* ]]; then + echo $($dotnet_root/dotnet tool uninstall $darc_cli_package_name --tool-path "$toolpath") + fi + fi + + local arcadeServicesSource="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" + + echo "Installing Darc CLI version $darcVersion..." + echo "You may need to restart your command shell if this is the first dotnet tool you have installed." + if [ -z "$toolpath" ]; then + echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g) + else + echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath") + fi +} + +InstallDarcCli diff --git a/eng/common/dotnet-install.cmd b/eng/common/dotnet-install.cmd new file mode 100644 index 000000000..b1c2642e7 --- /dev/null +++ b/eng/common/dotnet-install.cmd @@ -0,0 +1,2 @@ +@echo off +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0dotnet-install.ps1""" %*" \ No newline at end of file diff --git a/eng/common/dotnet-install.ps1 b/eng/common/dotnet-install.ps1 new file mode 100644 index 000000000..811f0f717 --- /dev/null +++ b/eng/common/dotnet-install.ps1 @@ -0,0 +1,28 @@ +[CmdletBinding(PositionalBinding=$false)] +Param( + [string] $verbosity = 'minimal', + [string] $architecture = '', + [string] $version = 'Latest', + [string] $runtime = 'dotnet', + [string] $RuntimeSourceFeed = '', + [string] $RuntimeSourceFeedKey = '' +) + +. $PSScriptRoot\tools.ps1 + +$dotnetRoot = Join-Path $RepoRoot '.dotnet' + +$installdir = $dotnetRoot +try { + if ($architecture -and $architecture.Trim() -eq 'x86') { + $installdir = Join-Path $installdir 'x86' + } + InstallDotNet $installdir $version $architecture $runtime $true -RuntimeSourceFeed $RuntimeSourceFeed -RuntimeSourceFeedKey $RuntimeSourceFeedKey +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ + ExitWithExitCode 1 +} + +ExitWithExitCode 0 diff --git a/eng/common/dotnet-install.sh b/eng/common/dotnet-install.sh new file mode 100755 index 000000000..7e69e3a9e --- /dev/null +++ b/eng/common/dotnet-install.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" +# resolve $source until the file is no longer a symlink +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +. "$scriptroot/tools.sh" + +version='Latest' +architecture='' +runtime='dotnet' +runtimeSourceFeed='' +runtimeSourceFeedKey='' +while [[ $# > 0 ]]; do + opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -version|-v) + shift + version="$1" + ;; + -architecture|-a) + shift + architecture="$1" + ;; + -runtime|-r) + shift + runtime="$1" + ;; + -runtimesourcefeed) + shift + runtimeSourceFeed="$1" + ;; + -runtimesourcefeedkey) + shift + runtimeSourceFeedKey="$1" + ;; + *) + Write-PipelineTelemetryError -Category 'Build' -Message "Invalid argument: $1" + exit 1 + ;; + esac + shift +done + +# Use uname to determine what the CPU is, see https://en.wikipedia.org/wiki/Uname#Examples +cpuname=$(uname -m) +case $cpuname in + arm64|aarch64) + buildarch=arm64 + if [ "$(getconf LONG_BIT)" -lt 64 ]; then + # This is 32-bit OS running on 64-bit CPU (for example Raspberry Pi OS) + buildarch=arm + fi + ;; + loongarch64) + buildarch=loongarch64 + ;; + amd64|x86_64) + buildarch=x64 + ;; + armv*l) + buildarch=arm + ;; + i[3-6]86) + buildarch=x86 + ;; + *) + echo "Unknown CPU $cpuname detected, treating it as x64" + buildarch=x64 + ;; +esac + +dotnetRoot="${repo_root}.dotnet" +if [[ $architecture != "" ]] && [[ $architecture != $buildarch ]]; then + dotnetRoot="$dotnetRoot/$architecture" +fi + +InstallDotNet $dotnetRoot $version "$architecture" $runtime true $runtimeSourceFeed $runtimeSourceFeedKey || { + local exit_code=$? + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "dotnet-install.sh failed (exit code '$exit_code')." >&2 + ExitWithExitCode $exit_code +} + +ExitWithExitCode 0 diff --git a/eng/common/enable-cross-org-publishing.ps1 b/eng/common/enable-cross-org-publishing.ps1 new file mode 100644 index 000000000..da09da4f1 --- /dev/null +++ b/eng/common/enable-cross-org-publishing.ps1 @@ -0,0 +1,13 @@ +param( + [string] $token +) + + +. $PSScriptRoot\pipeline-logging-functions.ps1 + +# Write-PipelineSetVariable will no-op if a variable named $ci is not defined +# Since this script is only ever called in AzDO builds, just universally set it +$ci = $true + +Write-PipelineSetVariable -Name 'VSS_NUGET_ACCESSTOKEN' -Value $token -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'VSS_NUGET_URI_PREFIXES' -Value 'https://dnceng.pkgs.visualstudio.com/;https://pkgs.dev.azure.com/dnceng/;https://devdiv.pkgs.visualstudio.com/;https://pkgs.dev.azure.com/devdiv/' -IsMultiJobVariable $false diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 new file mode 100644 index 000000000..524aaa57f --- /dev/null +++ b/eng/common/generate-locproject.ps1 @@ -0,0 +1,189 @@ +Param( + [Parameter(Mandatory=$true)][string] $SourcesDirectory, # Directory where source files live; if using a Localize directory it should live in here + [string] $LanguageSet = 'VS_Main_Languages', # Language set to be used in the LocProject.json + [switch] $UseCheckedInLocProjectJson, # When set, generates a LocProject.json and compares it to one that already exists in the repo; otherwise just generates one + [switch] $CreateNeutralXlfs # Creates neutral xlf files. Only set to false when running locally +) + +# Generates LocProject.json files for the OneLocBuild task. OneLocBuildTask is described here: +# https://ceapex.visualstudio.com/CEINTL/_wiki/wikis/CEINTL.wiki/107/Localization-with-OneLocBuild-Task + +Set-StrictMode -Version 2.0 +$ErrorActionPreference = "Stop" +. $PSScriptRoot\pipeline-logging-functions.ps1 + +$exclusionsFilePath = "$SourcesDirectory\eng\Localize\LocExclusions.json" +$exclusions = @{ Exclusions = @() } +if (Test-Path -Path $exclusionsFilePath) +{ + $exclusions = Get-Content "$exclusionsFilePath" | ConvertFrom-Json +} + +Push-Location "$SourcesDirectory" # push location for Resolve-Path -Relative to work + +# Template files +$jsonFiles = @() +$jsonTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\.+\.en\.json" } # .NET templating pattern +$jsonTemplateFiles | ForEach-Object { + $null = $_.Name -Match "(.+)\.[\w-]+\.json" # matches '[filename].[langcode].json + + $destinationFile = "$($_.Directory.FullName)\$($Matches.1).json" + $jsonFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru +} + +$jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern + +$wxlFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\.+\.wxl" -And -Not( $_.Directory.Name -Match "\d{4}" ) } # localized files live in four digit lang ID directories; this excludes them +if (-not $wxlFiles) { + $wxlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\1033\\.+\.wxl" } # pick up en files (1033 = en) specifically so we can copy them to use as the neutral xlf files + if ($wxlEnFiles) { + $wxlFiles = @() + $wxlEnFiles | ForEach-Object { + $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" + $wxlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru + } + } +} + +$macosHtmlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\.lproj\\.+\.html$" } # add installer HTML files +$macosHtmlFiles = @() +if ($macosHtmlEnFiles) { + $macosHtmlEnFiles | ForEach-Object { + $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" + $macosHtmlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru + } +} + +$xlfFiles = @() + +$allXlfFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory\*\*.xlf" +$langXlfFiles = @() +if ($allXlfFiles) { + $null = $allXlfFiles[0].FullName -Match "\.([\w-]+)\.xlf" # matches '[langcode].xlf' + $firstLangCode = $Matches.1 + $langXlfFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory\*\*.$firstLangCode.xlf" +} +$langXlfFiles | ForEach-Object { + $null = $_.Name -Match "(.+)\.[\w-]+\.xlf" # matches '[filename].[langcode].xlf + + $destinationFile = "$($_.Directory.FullName)\$($Matches.1).xlf" + $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru +} + +$locFiles = $jsonFiles + $jsonWinformsTemplateFiles + $xlfFiles + +$locJson = @{ + Projects = @( + @{ + LanguageSet = $LanguageSet + LocItems = @( + $locFiles | ForEach-Object { + $outputPath = "$(($_.DirectoryName | Resolve-Path -Relative) + "\")" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($_.FullName.Contains($exclusion)) + { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + if (!$CreateNeutralXlfs -and $_.Extension -eq '.xlf') { + Remove-Item -Path $sourceFile + } + if ($continue) + { + if ($_.Directory.Name -eq 'en' -and $_.Extension -eq '.json') { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnPath" + OutputPath = "$($_.Directory.Parent.FullName | Resolve-Path -Relative)\" + } + } else { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnName" + OutputPath = $outputPath + } + } + } + } + ) + }, + @{ + LanguageSet = $LanguageSet + CloneLanguageSet = "WiX_CloneLanguages" + LssFiles = @( "wxl_loc.lss" ) + LocItems = @( + $wxlFiles | ForEach-Object { + $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($_.FullName.Contains($exclusion)) { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + if ($continue) + { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnPath" + OutputPath = $outputPath + } + } + } + ) + }, + @{ + LanguageSet = $LanguageSet + CloneLanguageSet = "VS_macOS_CloneLanguages" + LssFiles = @( ".\eng\common\loc\P22DotNetHtmlLocalization.lss" ) + LocItems = @( + $macosHtmlFiles | ForEach-Object { + $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($_.FullName.Contains($exclusion)) { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + $lciFile = $sourceFile + ".lci" + if ($continue) { + $result = @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnPath" + OutputPath = $outputPath + } + if (Test-Path $lciFile -PathType Leaf) { + $result["LciFile"] = $lciFile + } + return $result + } + } + ) + } + ) +} + +$json = ConvertTo-Json $locJson -Depth 5 +Write-Host "LocProject.json generated:`n`n$json`n`n" +Pop-Location + +if (!$UseCheckedInLocProjectJson) { + New-Item "$SourcesDirectory\eng\Localize\LocProject.json" -Force # Need this to make sure the Localize directory is created + Set-Content "$SourcesDirectory\eng\Localize\LocProject.json" $json +} +else { + New-Item "$SourcesDirectory\eng\Localize\LocProject-generated.json" -Force # Need this to make sure the Localize directory is created + Set-Content "$SourcesDirectory\eng\Localize\LocProject-generated.json" $json + + if ((Get-FileHash "$SourcesDirectory\eng\Localize\LocProject-generated.json").Hash -ne (Get-FileHash "$SourcesDirectory\eng\Localize\LocProject.json").Hash) { + Write-PipelineTelemetryError -Category "OneLocBuild" -Message "Existing LocProject.json differs from generated LocProject.json. Download LocProject-generated.json and compare them." + + exit 1 + } + else { + Write-Host "Generated LocProject.json and current LocProject.json are identical." + } +} diff --git a/eng/common/generate-sbom-prep.ps1 b/eng/common/generate-sbom-prep.ps1 new file mode 100644 index 000000000..3e5c1c74a --- /dev/null +++ b/eng/common/generate-sbom-prep.ps1 @@ -0,0 +1,21 @@ +Param( + [Parameter(Mandatory=$true)][string] $ManifestDirPath # Manifest directory where sbom will be placed +) + +. $PSScriptRoot\pipeline-logging-functions.ps1 + +Write-Host "Creating dir $ManifestDirPath" +# create directory for sbom manifest to be placed +if (!(Test-Path -path $ManifestDirPath)) +{ + New-Item -ItemType Directory -path $ManifestDirPath + Write-Host "Successfully created directory $ManifestDirPath" +} +else{ + Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." +} + +Write-Host "Updating artifact name" +$artifact_name = "${env:SYSTEM_STAGENAME}_${env:AGENT_JOBNAME}_SBOM" -replace '["/:<>\\|?@*"() ]', '_' +Write-Host "Artifact name $artifact_name" +Write-Host "##vso[task.setvariable variable=ARTIFACT_NAME]$artifact_name" diff --git a/eng/common/generate-sbom-prep.sh b/eng/common/generate-sbom-prep.sh new file mode 100644 index 000000000..d5c76dc82 --- /dev/null +++ b/eng/common/generate-sbom-prep.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" + +# resolve $SOURCE until the file is no longer a symlink +while [[ -h $source ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" +. $scriptroot/pipeline-logging-functions.sh + +manifest_dir=$1 + +if [ ! -d "$manifest_dir" ] ; then + mkdir -p "$manifest_dir" + echo "Sbom directory created." $manifest_dir +else + Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." +fi + +artifact_name=$SYSTEM_STAGENAME"_"$AGENT_JOBNAME"_SBOM" +echo "Artifact name before : "$artifact_name +# replace all special characters with _, some builds use special characters like : in Agent.Jobname, that is not a permissible name while uploading artifacts. +safe_artifact_name="${artifact_name//["/:<>\\|?@*$" ]/_}" +echo "Artifact name after : "$safe_artifact_name +export ARTIFACT_NAME=$safe_artifact_name +echo "##vso[task.setvariable variable=ARTIFACT_NAME]$safe_artifact_name" + +exit 0 diff --git a/eng/common/helixpublish.proj b/eng/common/helixpublish.proj new file mode 100644 index 000000000..c1323bf41 --- /dev/null +++ b/eng/common/helixpublish.proj @@ -0,0 +1,27 @@ + + + + + msbuild + + + + + %(Identity) + + + + + + $(WorkItemDirectory) + $(WorkItemCommand) + $(WorkItemTimeout) + + + + + + + + + diff --git a/eng/common/init-tools-native.cmd b/eng/common/init-tools-native.cmd new file mode 100644 index 000000000..438cd548c --- /dev/null +++ b/eng/common/init-tools-native.cmd @@ -0,0 +1,3 @@ +@echo off +powershell -NoProfile -NoLogo -ExecutionPolicy ByPass -command "& """%~dp0init-tools-native.ps1""" %*" +exit /b %ErrorLevel% \ No newline at end of file diff --git a/eng/common/init-tools-native.ps1 b/eng/common/init-tools-native.ps1 new file mode 100644 index 000000000..27ccdb9ec --- /dev/null +++ b/eng/common/init-tools-native.ps1 @@ -0,0 +1,203 @@ +<# +.SYNOPSIS +Entry point script for installing native tools + +.DESCRIPTION +Reads $RepoRoot\global.json file to determine native assets to install +and executes installers for those tools + +.PARAMETER BaseUri +Base file directory or Url from which to acquire tool archives + +.PARAMETER InstallDirectory +Directory to install native toolset. This is a command-line override for the default +Install directory precedence order: +- InstallDirectory command-line override +- NETCOREENG_INSTALL_DIRECTORY environment variable +- (default) %USERPROFILE%/.netcoreeng/native + +.PARAMETER Clean +Switch specifying to not install anything, but cleanup native asset folders + +.PARAMETER Force +Clean and then install tools + +.PARAMETER DownloadRetries +Total number of retry attempts + +.PARAMETER RetryWaitTimeInSeconds +Wait time between retry attempts in seconds + +.PARAMETER GlobalJsonFile +File path to global.json file + +.PARAMETER PathPromotion +Optional switch to enable either promote native tools specified in the global.json to the path (in Azure Pipelines) +or break the build if a native tool is not found on the path (on a local dev machine) + +.NOTES +#> +[CmdletBinding(PositionalBinding=$false)] +Param ( + [string] $BaseUri = 'https://netcorenativeassets.blob.core.windows.net/resource-packages/external', + [string] $InstallDirectory, + [switch] $Clean = $False, + [switch] $Force = $False, + [int] $DownloadRetries = 5, + [int] $RetryWaitTimeInSeconds = 30, + [string] $GlobalJsonFile, + [switch] $PathPromotion +) + +if (!$GlobalJsonFile) { + $GlobalJsonFile = Join-Path (Get-Item $PSScriptRoot).Parent.Parent.FullName 'global.json' +} + +Set-StrictMode -version 2.0 +$ErrorActionPreference='Stop' + +. $PSScriptRoot\pipeline-logging-functions.ps1 +Import-Module -Name (Join-Path $PSScriptRoot 'native\CommonLibrary.psm1') + +try { + # Define verbose switch if undefined + $Verbose = $VerbosePreference -Eq 'Continue' + + $EngCommonBaseDir = Join-Path $PSScriptRoot 'native\' + $NativeBaseDir = $InstallDirectory + if (!$NativeBaseDir) { + $NativeBaseDir = CommonLibrary\Get-NativeInstallDirectory + } + $Env:CommonLibrary_NativeInstallDir = $NativeBaseDir + $InstallBin = Join-Path $NativeBaseDir 'bin' + $InstallerPath = Join-Path $EngCommonBaseDir 'install-tool.ps1' + + # Process tools list + Write-Host "Processing $GlobalJsonFile" + If (-Not (Test-Path $GlobalJsonFile)) { + Write-Host "Unable to find '$GlobalJsonFile'" + exit 0 + } + $NativeTools = Get-Content($GlobalJsonFile) -Raw | + ConvertFrom-Json | + Select-Object -Expand 'native-tools' -ErrorAction SilentlyContinue + if ($NativeTools) { + if ($PathPromotion -eq $True) { + $ArcadeToolsDirectory = "$env:SYSTEMDRIVE\arcade-tools" + if (Test-Path $ArcadeToolsDirectory) { # if this directory exists, we should use native tools on machine + $NativeTools.PSObject.Properties | ForEach-Object { + $ToolName = $_.Name + $ToolVersion = $_.Value + $InstalledTools = @{} + + if ((Get-Command "$ToolName" -ErrorAction SilentlyContinue) -eq $null) { + if ($ToolVersion -eq "latest") { + $ToolVersion = "" + } + $ToolDirectories = (Get-ChildItem -Path "$ArcadeToolsDirectory" -Filter "$ToolName-$ToolVersion*" | Sort-Object -Descending) + if ($ToolDirectories -eq $null) { + Write-Error "Unable to find directory for $ToolName $ToolVersion; please make sure the tool is installed on this image." + exit 1 + } + $ToolDirectory = $ToolDirectories[0] + $BinPathFile = "$($ToolDirectory.FullName)\binpath.txt" + if (-not (Test-Path -Path "$BinPathFile")) { + Write-Error "Unable to find binpath.txt in '$($ToolDirectory.FullName)' ($ToolName $ToolVersion); artifact is either installed incorrectly or is not a bootstrappable tool." + exit 1 + } + $BinPath = Get-Content "$BinPathFile" + $ToolPath = Convert-Path -Path $BinPath + Write-Host "Adding $ToolName to the path ($ToolPath)..." + Write-Host "##vso[task.prependpath]$ToolPath" + $env:PATH = "$ToolPath;$env:PATH" + $InstalledTools += @{ $ToolName = $ToolDirectory.FullName } + } + } + return $InstalledTools + } else { + $NativeTools.PSObject.Properties | ForEach-Object { + $ToolName = $_.Name + $ToolVersion = $_.Value + + if ((Get-Command "$ToolName" -ErrorAction SilentlyContinue) -eq $null) { + Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message "$ToolName not found on path. Please install $ToolName $ToolVersion before proceeding." + Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message "If this is running on a build machine, the arcade-tools directory was not found, which means there's an error with the image." + } + } + exit 0 + } + } else { + $NativeTools.PSObject.Properties | ForEach-Object { + $ToolName = $_.Name + $ToolVersion = $_.Value + $LocalInstallerArguments = @{ ToolName = "$ToolName" } + $LocalInstallerArguments += @{ InstallPath = "$InstallBin" } + $LocalInstallerArguments += @{ BaseUri = "$BaseUri" } + $LocalInstallerArguments += @{ CommonLibraryDirectory = "$EngCommonBaseDir" } + $LocalInstallerArguments += @{ Version = "$ToolVersion" } + + if ($Verbose) { + $LocalInstallerArguments += @{ Verbose = $True } + } + if (Get-Variable 'Force' -ErrorAction 'SilentlyContinue') { + if($Force) { + $LocalInstallerArguments += @{ Force = $True } + } + } + if ($Clean) { + $LocalInstallerArguments += @{ Clean = $True } + } + + Write-Verbose "Installing $ToolName version $ToolVersion" + Write-Verbose "Executing '$InstallerPath $($LocalInstallerArguments.Keys.ForEach({"-$_ '$($LocalInstallerArguments.$_)'"}) -join ' ')'" + & $InstallerPath @LocalInstallerArguments + if ($LASTEXITCODE -Ne "0") { + $errMsg = "$ToolName installation failed" + if ((Get-Variable 'DoNotAbortNativeToolsInstallationOnFailure' -ErrorAction 'SilentlyContinue') -and $DoNotAbortNativeToolsInstallationOnFailure) { + $showNativeToolsWarning = $true + if ((Get-Variable 'DoNotDisplayNativeToolsInstallationWarnings' -ErrorAction 'SilentlyContinue') -and $DoNotDisplayNativeToolsInstallationWarnings) { + $showNativeToolsWarning = $false + } + if ($showNativeToolsWarning) { + Write-Warning $errMsg + } + $toolInstallationFailure = $true + } else { + # We cannot change this to Write-PipelineTelemetryError because of https://github.com/dotnet/arcade/issues/4482 + Write-Host $errMsg + exit 1 + } + } + } + + if ((Get-Variable 'toolInstallationFailure' -ErrorAction 'SilentlyContinue') -and $toolInstallationFailure) { + # We cannot change this to Write-PipelineTelemetryError because of https://github.com/dotnet/arcade/issues/4482 + Write-Host 'Native tools bootstrap failed' + exit 1 + } + } + } + else { + Write-Host 'No native tools defined in global.json' + exit 0 + } + + if ($Clean) { + exit 0 + } + if (Test-Path $InstallBin) { + Write-Host 'Native tools are available from ' (Convert-Path -Path $InstallBin) + Write-Host "##vso[task.prependpath]$(Convert-Path -Path $InstallBin)" + return $InstallBin + } + elseif (-not ($PathPromotion)) { + Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message 'Native tools install directory does not exist, installation failed' + exit 1 + } + exit 0 +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/init-tools-native.sh b/eng/common/init-tools-native.sh new file mode 100755 index 000000000..3e6a8d6ac --- /dev/null +++ b/eng/common/init-tools-native.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +base_uri='https://netcorenativeassets.blob.core.windows.net/resource-packages/external' +install_directory='' +clean=false +force=false +download_retries=5 +retry_wait_time_seconds=30 +global_json_file="$(dirname "$(dirname "${scriptroot}")")/global.json" +declare -a native_assets + +. $scriptroot/pipeline-logging-functions.sh +. $scriptroot/native/common-library.sh + +while (($# > 0)); do + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" + case $lowerI in + --baseuri) + base_uri=$2 + shift 2 + ;; + --installdirectory) + install_directory=$2 + shift 2 + ;; + --clean) + clean=true + shift 1 + ;; + --force) + force=true + shift 1 + ;; + --donotabortonfailure) + donotabortonfailure=true + shift 1 + ;; + --donotdisplaywarnings) + donotdisplaywarnings=true + shift 1 + ;; + --downloadretries) + download_retries=$2 + shift 2 + ;; + --retrywaittimeseconds) + retry_wait_time_seconds=$2 + shift 2 + ;; + --help) + echo "Common settings:" + echo " --installdirectory Directory to install native toolset." + echo " This is a command-line override for the default" + echo " Install directory precedence order:" + echo " - InstallDirectory command-line override" + echo " - NETCOREENG_INSTALL_DIRECTORY environment variable" + echo " - (default) %USERPROFILE%/.netcoreeng/native" + echo "" + echo " --clean Switch specifying not to install anything, but cleanup native asset folders" + echo " --donotabortonfailure Switch specifiying whether to abort native tools installation on failure" + echo " --donotdisplaywarnings Switch specifiying whether to display warnings during native tools installation on failure" + echo " --force Clean and then install tools" + echo " --help Print help and exit" + echo "" + echo "Advanced settings:" + echo " --baseuri Base URI for where to download native tools from" + echo " --downloadretries Number of times a download should be attempted" + echo " --retrywaittimeseconds Wait time between download attempts" + echo "" + exit 0 + ;; + esac +done + +function ReadGlobalJsonNativeTools { + # happy path: we have a proper JSON parsing tool `jq(1)` in PATH! + if command -v jq &> /dev/null; then + + # jq: read each key/value pair under "native-tools" entry and emit: + # KEY="" VALUE="" + # followed by a null byte. + # + # bash: read line with null byte delimeter and push to array (for later `eval`uation). + + while IFS= read -rd '' line; do + native_assets+=("$line") + done < <(jq -r '. | + select(has("native-tools")) | + ."native-tools" | + keys[] as $k | + @sh "KEY=\($k) VALUE=\(.[$k])\u0000"' "$global_json_file") + + return + fi + + # Warning: falling back to manually parsing JSON, which is not recommended. + + # Following routine matches the output and escaping logic of jq(1)'s @sh formatter used above. + # It has been tested with several weird strings with escaped characters in entries (key and value) + # and results were compared with the output of jq(1) in binary representation using xxd(1); + # just before the assignment to 'native_assets' array (above and below). + + # try to capture the section under "native-tools". + if [[ ! "$(cat "$global_json_file")" =~ \"native-tools\"[[:space:]\:\{]*([^\}]+) ]]; then + return + fi + + section="${BASH_REMATCH[1]}" + + parseStarted=0 + possibleEnd=0 + escaping=0 + escaped=0 + isKey=1 + + for (( i=0; i<${#section}; i++ )); do + char="${section:$i:1}" + if ! ((parseStarted)) && [[ "$char" =~ [[:space:],:] ]]; then continue; fi + + if ! ((escaping)) && [[ "$char" == "\\" ]]; then + escaping=1 + elif ((escaping)) && ! ((escaped)); then + escaped=1 + fi + + if ! ((parseStarted)) && [[ "$char" == "\"" ]]; then + parseStarted=1 + possibleEnd=0 + elif [[ "$char" == "'" ]]; then + token="$token'\\\''" + possibleEnd=0 + elif ((escaping)) || [[ "$char" != "\"" ]]; then + token="$token$char" + possibleEnd=1 + fi + + if ((possibleEnd)) && ! ((escaping)) && [[ "$char" == "\"" ]]; then + # Use printf to unescape token to match jq(1)'s @sh formatting rules. + # do not use 'token="$(printf "$token")"' syntax, as $() eats the trailing linefeed. + printf -v token "'$token'" + + if ((isKey)); then + KEY="$token" + isKey=0 + else + line="KEY=$KEY VALUE=$token" + native_assets+=("$line") + isKey=1 + fi + + # reset for next token + parseStarted=0 + token= + elif ((escaping)) && ((escaped)); then + escaping=0 + escaped=0 + fi + done +} + +native_base_dir=$install_directory +if [[ -z $install_directory ]]; then + native_base_dir=$(GetNativeInstallDirectory) +fi + +install_bin="${native_base_dir}/bin" +installed_any=false + +ReadGlobalJsonNativeTools + +if [[ ${#native_assets[@]} -eq 0 ]]; then + echo "No native tools defined in global.json" + exit 0; +else + native_installer_dir="$scriptroot/native" + for index in "${!native_assets[@]}"; do + eval "${native_assets["$index"]}" + + installer_path="$native_installer_dir/install-$KEY.sh" + installer_command="$installer_path" + installer_command+=" --baseuri $base_uri" + installer_command+=" --installpath $install_bin" + installer_command+=" --version $VALUE" + echo $installer_command + + if [[ $force = true ]]; then + installer_command+=" --force" + fi + + if [[ $clean = true ]]; then + installer_command+=" --clean" + fi + + if [[ -a $installer_path ]]; then + $installer_command + if [[ $? != 0 ]]; then + if [[ $donotabortonfailure = true ]]; then + if [[ $donotdisplaywarnings != true ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed" + fi + else + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed" + exit 1 + fi + else + $installed_any = true + fi + else + if [[ $donotabortonfailure == true ]]; then + if [[ $donotdisplaywarnings != true ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed: no install script" + fi + else + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed: no install script" + exit 1 + fi + fi + done +fi + +if [[ $clean = true ]]; then + exit 0 +fi + +if [[ -d $install_bin ]]; then + echo "Native tools are available from $install_bin" + echo "##vso[task.prependpath]$install_bin" +else + if [[ $installed_any = true ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Native tools install directory does not exist, installation failed" + exit 1 + fi +fi + +exit 0 diff --git a/eng/common/internal-feed-operations.ps1 b/eng/common/internal-feed-operations.ps1 new file mode 100644 index 000000000..92b77347d --- /dev/null +++ b/eng/common/internal-feed-operations.ps1 @@ -0,0 +1,132 @@ +param( + [Parameter(Mandatory=$true)][string] $Operation, + [string] $AuthToken, + [string] $CommitSha, + [string] $RepoName, + [switch] $IsFeedPrivate +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 +. $PSScriptRoot\tools.ps1 + +# Sets VSS_NUGET_EXTERNAL_FEED_ENDPOINTS based on the "darc-int-*" feeds defined in NuGet.config. This is needed +# in build agents by CredProvider to authenticate the restore requests to internal feeds as specified in +# https://github.com/microsoft/artifacts-credprovider/blob/0f53327cd12fd893d8627d7b08a2171bf5852a41/README.md#environment-variables. This should ONLY be called from identified +# internal builds +function SetupCredProvider { + param( + [string] $AuthToken + ) + + # Install the Cred Provider NuGet plugin + Write-Host 'Setting up Cred Provider NuGet plugin in the agent...' + Write-Host "Getting 'installcredprovider.ps1' from 'https://github.com/microsoft/artifacts-credprovider'..." + + $url = 'https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1' + + Write-Host "Writing the contents of 'installcredprovider.ps1' locally..." + Invoke-WebRequest $url -OutFile installcredprovider.ps1 + + Write-Host 'Installing plugin...' + .\installcredprovider.ps1 -Force + + Write-Host "Deleting local copy of 'installcredprovider.ps1'..." + Remove-Item .\installcredprovider.ps1 + + if (-Not("$env:USERPROFILE\.nuget\plugins\netcore")) { + Write-PipelineTelemetryError -Category 'Arcade' -Message 'CredProvider plugin was not installed correctly!' + ExitWithExitCode 1 + } + else { + Write-Host 'CredProvider plugin was installed correctly!' + } + + # Then, we set the 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS' environment variable to restore from the stable + # feeds successfully + + $nugetConfigPath = Join-Path $RepoRoot "NuGet.config" + + if (-Not (Test-Path -Path $nugetConfigPath)) { + Write-PipelineTelemetryError -Category 'Build' -Message 'NuGet.config file not found in repo root!' + ExitWithExitCode 1 + } + + $endpoints = New-Object System.Collections.ArrayList + $nugetConfigPackageSources = Select-Xml -Path $nugetConfigPath -XPath "//packageSources/add[contains(@key, 'darc-int-')]/@value" | foreach{$_.Node.Value} + + if (($nugetConfigPackageSources | Measure-Object).Count -gt 0 ) { + foreach ($stableRestoreResource in $nugetConfigPackageSources) { + $trimmedResource = ([string]$stableRestoreResource).Trim() + [void]$endpoints.Add(@{endpoint="$trimmedResource"; password="$AuthToken"}) + } + } + + if (($endpoints | Measure-Object).Count -gt 0) { + $endpointCredentials = @{endpointCredentials=$endpoints} | ConvertTo-Json -Compress + + # Create the environment variables the AzDo way + Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data $endpointCredentials -Properties @{ + 'variable' = 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS' + 'issecret' = 'false' + } + + # We don't want sessions cached since we will be updating the endpoints quite frequently + Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data 'False' -Properties @{ + 'variable' = 'NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED' + 'issecret' = 'false' + } + } + else + { + Write-Host 'No internal endpoints found in NuGet.config' + } +} + +#Workaround for https://github.com/microsoft/msbuild/issues/4430 +function InstallDotNetSdkAndRestoreArcade { + $dotnetTempDir = Join-Path $RepoRoot "dotnet" + $dotnetSdkVersion="2.1.507" # After experimentation we know this version works when restoring the SDK (compared to 3.0.*) + $dotnet = "$dotnetTempDir\dotnet.exe" + $restoreProjPath = "$PSScriptRoot\restore.proj" + + Write-Host "Installing dotnet SDK version $dotnetSdkVersion to restore Arcade SDK..." + InstallDotNetSdk "$dotnetTempDir" "$dotnetSdkVersion" + + '' | Out-File "$restoreProjPath" + + & $dotnet restore $restoreProjPath + + Write-Host 'Arcade SDK restored!' + + if (Test-Path -Path $restoreProjPath) { + Remove-Item $restoreProjPath + } + + if (Test-Path -Path $dotnetTempDir) { + Remove-Item $dotnetTempDir -Recurse + } +} + +try { + Push-Location $PSScriptRoot + + if ($Operation -like 'setup') { + SetupCredProvider $AuthToken + } + elseif ($Operation -like 'install-restore') { + InstallDotNetSdkAndRestoreArcade + } + else { + Write-PipelineTelemetryError -Category 'Arcade' -Message "Unknown operation '$Operation'!" + ExitWithExitCode 1 + } +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'Arcade' -Message $_ + ExitWithExitCode 1 +} +finally { + Pop-Location +} diff --git a/eng/common/internal-feed-operations.sh b/eng/common/internal-feed-operations.sh new file mode 100755 index 000000000..9378223ba --- /dev/null +++ b/eng/common/internal-feed-operations.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +set -e + +# Sets VSS_NUGET_EXTERNAL_FEED_ENDPOINTS based on the "darc-int-*" feeds defined in NuGet.config. This is needed +# in build agents by CredProvider to authenticate the restore requests to internal feeds as specified in +# https://github.com/microsoft/artifacts-credprovider/blob/0f53327cd12fd893d8627d7b08a2171bf5852a41/README.md#environment-variables. +# This should ONLY be called from identified internal builds +function SetupCredProvider { + local authToken=$1 + + # Install the Cred Provider NuGet plugin + echo "Setting up Cred Provider NuGet plugin in the agent..."... + echo "Getting 'installcredprovider.ps1' from 'https://github.com/microsoft/artifacts-credprovider'..." + + local url="https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh" + + echo "Writing the contents of 'installcredprovider.ps1' locally..." + local installcredproviderPath="installcredprovider.sh" + if command -v curl > /dev/null; then + curl $url > "$installcredproviderPath" + else + wget -q -O "$installcredproviderPath" "$url" + fi + + echo "Installing plugin..." + . "$installcredproviderPath" + + echo "Deleting local copy of 'installcredprovider.sh'..." + rm installcredprovider.sh + + if [ ! -d "$HOME/.nuget/plugins" ]; then + Write-PipelineTelemetryError -category 'Build' 'CredProvider plugin was not installed correctly!' + ExitWithExitCode 1 + else + echo "CredProvider plugin was installed correctly!" + fi + + # Then, we set the 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS' environment variable to restore from the stable + # feeds successfully + + local nugetConfigPath="{$repo_root}NuGet.config" + + if [ ! "$nugetConfigPath" ]; then + Write-PipelineTelemetryError -category 'Build' "NuGet.config file not found in repo's root!" + ExitWithExitCode 1 + fi + + local endpoints='[' + local nugetConfigPackageValues=`cat "$nugetConfigPath" | grep "key=\"darc-int-"` + local pattern="value=\"(.*)\"" + + for value in $nugetConfigPackageValues + do + if [[ $value =~ $pattern ]]; then + local endpoint="${BASH_REMATCH[1]}" + endpoints+="{\"endpoint\": \"$endpoint\", \"password\": \"$authToken\"}," + fi + done + + endpoints=${endpoints%?} + endpoints+=']' + + if [ ${#endpoints} -gt 2 ]; then + local endpointCredentials="{\"endpointCredentials\": "$endpoints"}" + + echo "##vso[task.setvariable variable=VSS_NUGET_EXTERNAL_FEED_ENDPOINTS]$endpointCredentials" + echo "##vso[task.setvariable variable=NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED]False" + else + echo "No internal endpoints found in NuGet.config" + fi +} + +# Workaround for https://github.com/microsoft/msbuild/issues/4430 +function InstallDotNetSdkAndRestoreArcade { + local dotnetTempDir="$repo_root/dotnet" + local dotnetSdkVersion="2.1.507" # After experimentation we know this version works when restoring the SDK (compared to 3.0.*) + local restoreProjPath="$repo_root/eng/common/restore.proj" + + echo "Installing dotnet SDK version $dotnetSdkVersion to restore Arcade SDK..." + echo "" > "$restoreProjPath" + + InstallDotNetSdk "$dotnetTempDir" "$dotnetSdkVersion" + + local res=`$dotnetTempDir/dotnet restore $restoreProjPath` + echo "Arcade SDK restored!" + + # Cleanup + if [ "$restoreProjPath" ]; then + rm "$restoreProjPath" + fi + + if [ "$dotnetTempDir" ]; then + rm -r $dotnetTempDir + fi +} + +source="${BASH_SOURCE[0]}" +operation='' +authToken='' +repoName='' + +while [[ $# > 0 ]]; do + opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + --operation) + operation=$2 + shift + ;; + --authtoken) + authToken=$2 + shift + ;; + *) + echo "Invalid argument: $1" + usage + exit 1 + ;; + esac + + shift +done + +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +. "$scriptroot/tools.sh" + +if [ "$operation" = "setup" ]; then + SetupCredProvider $authToken +elif [ "$operation" = "install-restore" ]; then + InstallDotNetSdkAndRestoreArcade +else + echo "Unknown operation '$operation'!" +fi diff --git a/eng/common/internal/Directory.Build.props b/eng/common/internal/Directory.Build.props new file mode 100644 index 000000000..f1d041c33 --- /dev/null +++ b/eng/common/internal/Directory.Build.props @@ -0,0 +1,11 @@ + + + + + false + false + + + + + diff --git a/eng/common/internal/NuGet.config b/eng/common/internal/NuGet.config new file mode 100644 index 000000000..19d3d311b --- /dev/null +++ b/eng/common/internal/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj new file mode 100644 index 000000000..e925952d5 --- /dev/null +++ b/eng/common/internal/Tools.csproj @@ -0,0 +1,31 @@ + + + + + net472 + false + + + + + + + + + + + + + + https://devdiv.pkgs.visualstudio.com/_packaging/dotnet-core-internal-tooling/nuget/v3/index.json; + + + $(RestoreSources); + https://devdiv.pkgs.visualstudio.com/_packaging/VS/nuget/v3/index.json; + + + + + + + diff --git a/eng/common/loc/P22DotNetHtmlLocalization.lss b/eng/common/loc/P22DotNetHtmlLocalization.lss new file mode 100644 index 000000000..5d892d619 --- /dev/null +++ b/eng/common/loc/P22DotNetHtmlLocalization.lss @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/common/msbuild.ps1 b/eng/common/msbuild.ps1 new file mode 100644 index 000000000..f041e5ddd --- /dev/null +++ b/eng/common/msbuild.ps1 @@ -0,0 +1,28 @@ +[CmdletBinding(PositionalBinding=$false)] +Param( + [string] $verbosity = 'minimal', + [bool] $warnAsError = $true, + [bool] $nodeReuse = $true, + [switch] $ci, + [switch] $prepareMachine, + [switch] $excludePrereleaseVS, + [string] $msbuildEngine = $null, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$extraArgs +) + +. $PSScriptRoot\tools.ps1 + +try { + if ($ci) { + $nodeReuse = $false + } + + MSBuild @extraArgs +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'Build' -Message $_ + ExitWithExitCode 1 +} + +ExitWithExitCode 0 \ No newline at end of file diff --git a/eng/common/msbuild.sh b/eng/common/msbuild.sh new file mode 100755 index 000000000..20d3dad54 --- /dev/null +++ b/eng/common/msbuild.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" + +# resolve $source until the file is no longer a symlink +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +verbosity='minimal' +warn_as_error=true +node_reuse=true +prepare_machine=false +extra_args='' + +while (($# > 0)); do + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" + case $lowerI in + --verbosity) + verbosity=$2 + shift 2 + ;; + --warnaserror) + warn_as_error=$2 + shift 2 + ;; + --nodereuse) + node_reuse=$2 + shift 2 + ;; + --ci) + ci=true + shift 1 + ;; + --preparemachine) + prepare_machine=true + shift 1 + ;; + *) + extra_args="$extra_args $1" + shift 1 + ;; + esac +done + +. "$scriptroot/tools.sh" + +if [[ "$ci" == true ]]; then + node_reuse=false +fi + +MSBuild $extra_args +ExitWithExitCode 0 diff --git a/eng/common/native/CommonLibrary.psm1 b/eng/common/native/CommonLibrary.psm1 new file mode 100644 index 000000000..ca38268c4 --- /dev/null +++ b/eng/common/native/CommonLibrary.psm1 @@ -0,0 +1,400 @@ +<# +.SYNOPSIS +Helper module to install an archive to a directory + +.DESCRIPTION +Helper module to download and extract an archive to a specified directory + +.PARAMETER Uri +Uri of artifact to download + +.PARAMETER InstallDirectory +Directory to extract artifact contents to + +.PARAMETER Force +Force download / extraction if file or contents already exist. Default = False + +.PARAMETER DownloadRetries +Total number of retry attempts. Default = 5 + +.PARAMETER RetryWaitTimeInSeconds +Wait time between retry attempts in seconds. Default = 30 + +.NOTES +Returns False if download or extraction fail, True otherwise +#> +function DownloadAndExtract { + [CmdletBinding(PositionalBinding=$false)] + Param ( + [Parameter(Mandatory=$True)] + [string] $Uri, + [Parameter(Mandatory=$True)] + [string] $InstallDirectory, + [switch] $Force = $False, + [int] $DownloadRetries = 5, + [int] $RetryWaitTimeInSeconds = 30 + ) + # Define verbose switch if undefined + $Verbose = $VerbosePreference -Eq "Continue" + + $TempToolPath = CommonLibrary\Get-TempPathFilename -Path $Uri + + # Download native tool + $DownloadStatus = CommonLibrary\Get-File -Uri $Uri ` + -Path $TempToolPath ` + -DownloadRetries $DownloadRetries ` + -RetryWaitTimeInSeconds $RetryWaitTimeInSeconds ` + -Force:$Force ` + -Verbose:$Verbose + + if ($DownloadStatus -Eq $False) { + Write-Error "Download failed from $Uri" + return $False + } + + # Extract native tool + $UnzipStatus = CommonLibrary\Expand-Zip -ZipPath $TempToolPath ` + -OutputDirectory $InstallDirectory ` + -Force:$Force ` + -Verbose:$Verbose + + if ($UnzipStatus -Eq $False) { + # Retry Download one more time with Force=true + $DownloadRetryStatus = CommonLibrary\Get-File -Uri $Uri ` + -Path $TempToolPath ` + -DownloadRetries 1 ` + -RetryWaitTimeInSeconds $RetryWaitTimeInSeconds ` + -Force:$True ` + -Verbose:$Verbose + + if ($DownloadRetryStatus -Eq $False) { + Write-Error "Last attempt of download failed as well" + return $False + } + + # Retry unzip again one more time with Force=true + $UnzipRetryStatus = CommonLibrary\Expand-Zip -ZipPath $TempToolPath ` + -OutputDirectory $InstallDirectory ` + -Force:$True ` + -Verbose:$Verbose + if ($UnzipRetryStatus -Eq $False) + { + Write-Error "Last attempt of unzip failed as well" + # Clean up partial zips and extracts + if (Test-Path $TempToolPath) { + Remove-Item $TempToolPath -Force + } + if (Test-Path $InstallDirectory) { + Remove-Item $InstallDirectory -Force -Recurse + } + return $False + } + } + + return $True +} + +<# +.SYNOPSIS +Download a file, retry on failure + +.DESCRIPTION +Download specified file and retry if attempt fails + +.PARAMETER Uri +Uri of file to download. If Uri is a local path, the file will be copied instead of downloaded + +.PARAMETER Path +Path to download or copy uri file to + +.PARAMETER Force +Overwrite existing file if present. Default = False + +.PARAMETER DownloadRetries +Total number of retry attempts. Default = 5 + +.PARAMETER RetryWaitTimeInSeconds +Wait time between retry attempts in seconds Default = 30 + +#> +function Get-File { + [CmdletBinding(PositionalBinding=$false)] + Param ( + [Parameter(Mandatory=$True)] + [string] $Uri, + [Parameter(Mandatory=$True)] + [string] $Path, + [int] $DownloadRetries = 5, + [int] $RetryWaitTimeInSeconds = 30, + [switch] $Force = $False + ) + $Attempt = 0 + + if ($Force) { + if (Test-Path $Path) { + Remove-Item $Path -Force + } + } + if (Test-Path $Path) { + Write-Host "File '$Path' already exists, skipping download" + return $True + } + + $DownloadDirectory = Split-Path -ErrorAction Ignore -Path "$Path" -Parent + if (-Not (Test-Path $DownloadDirectory)) { + New-Item -path $DownloadDirectory -force -itemType "Directory" | Out-Null + } + + $TempPath = "$Path.tmp" + if (Test-Path -IsValid -Path $Uri) { + Write-Verbose "'$Uri' is a file path, copying temporarily to '$TempPath'" + Copy-Item -Path $Uri -Destination $TempPath + Write-Verbose "Moving temporary file to '$Path'" + Move-Item -Path $TempPath -Destination $Path + return $? + } + else { + Write-Verbose "Downloading $Uri" + # Don't display the console progress UI - it's a huge perf hit + $ProgressPreference = 'SilentlyContinue' + while($Attempt -Lt $DownloadRetries) + { + try { + Invoke-WebRequest -UseBasicParsing -Uri $Uri -OutFile $TempPath + Write-Verbose "Downloaded to temporary location '$TempPath'" + Move-Item -Path $TempPath -Destination $Path + Write-Verbose "Moved temporary file to '$Path'" + return $True + } + catch { + $Attempt++ + if ($Attempt -Lt $DownloadRetries) { + $AttemptsLeft = $DownloadRetries - $Attempt + Write-Warning "Download failed, $AttemptsLeft attempts remaining, will retry in $RetryWaitTimeInSeconds seconds" + Start-Sleep -Seconds $RetryWaitTimeInSeconds + } + else { + Write-Error $_ + Write-Error $_.Exception + } + } + } + } + + return $False +} + +<# +.SYNOPSIS +Generate a shim for a native tool + +.DESCRIPTION +Creates a wrapper script (shim) that passes arguments forward to native tool assembly + +.PARAMETER ShimName +The name of the shim + +.PARAMETER ShimDirectory +The directory where shims are stored + +.PARAMETER ToolFilePath +Path to file that shim forwards to + +.PARAMETER Force +Replace shim if already present. Default = False + +.NOTES +Returns $True if generating shim succeeds, $False otherwise +#> +function New-ScriptShim { + [CmdletBinding(PositionalBinding=$false)] + Param ( + [Parameter(Mandatory=$True)] + [string] $ShimName, + [Parameter(Mandatory=$True)] + [string] $ShimDirectory, + [Parameter(Mandatory=$True)] + [string] $ToolFilePath, + [Parameter(Mandatory=$True)] + [string] $BaseUri, + [switch] $Force + ) + try { + Write-Verbose "Generating '$ShimName' shim" + + if (-Not (Test-Path $ToolFilePath)){ + Write-Error "Specified tool file path '$ToolFilePath' does not exist" + return $False + } + + # WinShimmer is a small .NET Framework program that creates .exe shims to bootstrapped programs + # Many of the checks for installed programs expect a .exe extension for Windows tools, rather + # than a .bat or .cmd file. + # Source: https://github.com/dotnet/arcade/tree/master/src/WinShimmer + if (-Not (Test-Path "$ShimDirectory\WinShimmer\winshimmer.exe")) { + $InstallStatus = DownloadAndExtract -Uri "$BaseUri/windows/winshimmer/WinShimmer.zip" ` + -InstallDirectory $ShimDirectory\WinShimmer ` + -Force:$Force ` + -DownloadRetries 2 ` + -RetryWaitTimeInSeconds 5 ` + -Verbose:$Verbose + } + + if ((Test-Path (Join-Path $ShimDirectory "$ShimName.exe"))) { + Write-Host "$ShimName.exe already exists; replacing..." + Remove-Item (Join-Path $ShimDirectory "$ShimName.exe") + } + + & "$ShimDirectory\WinShimmer\winshimmer.exe" $ShimName $ToolFilePath $ShimDirectory + return $True + } + catch { + Write-Host $_ + Write-Host $_.Exception + return $False + } +} + +<# +.SYNOPSIS +Returns the machine architecture of the host machine + +.NOTES +Returns 'x64' on 64 bit machines + Returns 'x86' on 32 bit machines +#> +function Get-MachineArchitecture { + $ProcessorArchitecture = $Env:PROCESSOR_ARCHITECTURE + $ProcessorArchitectureW6432 = $Env:PROCESSOR_ARCHITEW6432 + if($ProcessorArchitecture -Eq "X86") + { + if(($ProcessorArchitectureW6432 -Eq "") -Or + ($ProcessorArchitectureW6432 -Eq "X86")) { + return "x86" + } + $ProcessorArchitecture = $ProcessorArchitectureW6432 + } + if (($ProcessorArchitecture -Eq "AMD64") -Or + ($ProcessorArchitecture -Eq "IA64") -Or + ($ProcessorArchitecture -Eq "ARM64") -Or + ($ProcessorArchitecture -Eq "LOONGARCH64")) { + return "x64" + } + return "x86" +} + +<# +.SYNOPSIS +Get the name of a temporary folder under the native install directory +#> +function Get-TempDirectory { + return Join-Path (Get-NativeInstallDirectory) "temp/" +} + +function Get-TempPathFilename { + [CmdletBinding(PositionalBinding=$false)] + Param ( + [Parameter(Mandatory=$True)] + [string] $Path + ) + $TempDir = CommonLibrary\Get-TempDirectory + $TempFilename = Split-Path $Path -leaf + $TempPath = Join-Path $TempDir $TempFilename + return $TempPath +} + +<# +.SYNOPSIS +Returns the base directory to use for native tool installation + +.NOTES +Returns the value of the NETCOREENG_INSTALL_DIRECTORY if that environment variable +is set, or otherwise returns an install directory under the %USERPROFILE% +#> +function Get-NativeInstallDirectory { + $InstallDir = $Env:NETCOREENG_INSTALL_DIRECTORY + if (!$InstallDir) { + $InstallDir = Join-Path $Env:USERPROFILE ".netcoreeng/native/" + } + return $InstallDir +} + +<# +.SYNOPSIS +Unzip an archive + +.DESCRIPTION +Powershell module to unzip an archive to a specified directory + +.PARAMETER ZipPath (Required) +Path to archive to unzip + +.PARAMETER OutputDirectory (Required) +Output directory for archive contents + +.PARAMETER Force +Overwrite output directory contents if they already exist + +.NOTES +- Returns True and does not perform an extraction if output directory already exists but Overwrite is not True. +- Returns True if unzip operation is successful +- Returns False if Overwrite is True and it is unable to remove contents of OutputDirectory +- Returns False if unable to extract zip archive +#> +function Expand-Zip { + [CmdletBinding(PositionalBinding=$false)] + Param ( + [Parameter(Mandatory=$True)] + [string] $ZipPath, + [Parameter(Mandatory=$True)] + [string] $OutputDirectory, + [switch] $Force + ) + + Write-Verbose "Extracting '$ZipPath' to '$OutputDirectory'" + try { + if ((Test-Path $OutputDirectory) -And (-Not $Force)) { + Write-Host "Directory '$OutputDirectory' already exists, skipping extract" + return $True + } + if (Test-Path $OutputDirectory) { + Write-Verbose "'Force' is 'True', but '$OutputDirectory' exists, removing directory" + Remove-Item $OutputDirectory -Force -Recurse + if ($? -Eq $False) { + Write-Error "Unable to remove '$OutputDirectory'" + return $False + } + } + + $TempOutputDirectory = Join-Path "$(Split-Path -Parent $OutputDirectory)" "$(Split-Path -Leaf $OutputDirectory).tmp" + if (Test-Path $TempOutputDirectory) { + Remove-Item $TempOutputDirectory -Force -Recurse + } + New-Item -Path $TempOutputDirectory -Force -ItemType "Directory" | Out-Null + + Add-Type -assembly "system.io.compression.filesystem" + [io.compression.zipfile]::ExtractToDirectory("$ZipPath", "$TempOutputDirectory") + if ($? -Eq $False) { + Write-Error "Unable to extract '$ZipPath'" + return $False + } + + Move-Item -Path $TempOutputDirectory -Destination $OutputDirectory + } + catch { + Write-Host $_ + Write-Host $_.Exception + + return $False + } + return $True +} + +export-modulemember -function DownloadAndExtract +export-modulemember -function Expand-Zip +export-modulemember -function Get-File +export-modulemember -function Get-MachineArchitecture +export-modulemember -function Get-NativeInstallDirectory +export-modulemember -function Get-TempDirectory +export-modulemember -function Get-TempPathFilename +export-modulemember -function New-ScriptShim diff --git a/eng/common/native/common-library.sh b/eng/common/native/common-library.sh new file mode 100755 index 000000000..080c2c283 --- /dev/null +++ b/eng/common/native/common-library.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash + +function GetNativeInstallDirectory { + local install_dir + + if [[ -z $NETCOREENG_INSTALL_DIRECTORY ]]; then + install_dir=$HOME/.netcoreeng/native/ + else + install_dir=$NETCOREENG_INSTALL_DIRECTORY + fi + + echo $install_dir + return 0 +} + +function GetTempDirectory { + + echo $(GetNativeInstallDirectory)temp/ + return 0 +} + +function ExpandZip { + local zip_path=$1 + local output_directory=$2 + local force=${3:-false} + + echo "Extracting $zip_path to $output_directory" + if [[ -d $output_directory ]] && [[ $force = false ]]; then + echo "Directory '$output_directory' already exists, skipping extract" + return 0 + fi + + if [[ -d $output_directory ]]; then + echo "'Force flag enabled, but '$output_directory' exists. Removing directory" + rm -rf $output_directory + if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Unable to remove '$output_directory'" + return 1 + fi + fi + + echo "Creating directory: '$output_directory'" + mkdir -p $output_directory + + echo "Extracting archive" + tar -xf $zip_path -C $output_directory + if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Unable to extract '$zip_path'" + return 1 + fi + + return 0 +} + +function GetCurrentOS { + local unameOut="$(uname -s)" + case $unameOut in + Linux*) echo "Linux";; + Darwin*) echo "MacOS";; + esac + return 0 +} + +function GetFile { + local uri=$1 + local path=$2 + local force=${3:-false} + local download_retries=${4:-5} + local retry_wait_time_seconds=${5:-30} + + if [[ -f $path ]]; then + if [[ $force = false ]]; then + echo "File '$path' already exists. Skipping download" + return 0 + else + rm -rf $path + fi + fi + + if [[ -f $uri ]]; then + echo "'$uri' is a file path, copying file to '$path'" + cp $uri $path + return $? + fi + + echo "Downloading $uri" + # Use curl if available, otherwise use wget + if command -v curl > /dev/null; then + curl "$uri" -sSL --retry $download_retries --retry-delay $retry_wait_time_seconds --create-dirs -o "$path" --fail + else + wget -q -O "$path" "$uri" --tries="$download_retries" + fi + + return $? +} + +function GetTempPathFileName { + local path=$1 + + local temp_dir=$(GetTempDirectory) + local temp_file_name=$(basename $path) + echo $temp_dir$temp_file_name + return 0 +} + +function DownloadAndExtract { + local uri=$1 + local installDir=$2 + local force=${3:-false} + local download_retries=${4:-5} + local retry_wait_time_seconds=${5:-30} + + local temp_tool_path=$(GetTempPathFileName $uri) + + echo "downloading to: $temp_tool_path" + + # Download file + GetFile "$uri" "$temp_tool_path" $force $download_retries $retry_wait_time_seconds + if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Failed to download '$uri' to '$temp_tool_path'." + return 1 + fi + + # Extract File + echo "extracting from $temp_tool_path to $installDir" + ExpandZip "$temp_tool_path" "$installDir" $force $download_retries $retry_wait_time_seconds + if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Failed to extract '$temp_tool_path' to '$installDir'." + return 1 + fi + + return 0 +} + +function NewScriptShim { + local shimpath=$1 + local tool_file_path=$2 + local force=${3:-false} + + echo "Generating '$shimpath' shim" + if [[ -f $shimpath ]]; then + if [[ $force = false ]]; then + echo "File '$shimpath' already exists." >&2 + return 1 + else + rm -rf $shimpath + fi + fi + + if [[ ! -f $tool_file_path ]]; then + # try to see if the path is lower cased + tool_file_path="$(echo $tool_file_path | tr "[:upper:]" "[:lower:]")" + if [[ ! -f $tool_file_path ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Specified tool file path:'$tool_file_path' does not exist" + return 1 + fi + fi + + local shim_contents=$'#!/usr/bin/env bash\n' + shim_contents+="SHIMARGS="$'$1\n' + shim_contents+="$tool_file_path"$' $SHIMARGS\n' + + # Write shim file + echo "$shim_contents" > $shimpath + + chmod +x $shimpath + + echo "Finished generating shim '$shimpath'" + + return $? +} + diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh new file mode 100644 index 000000000..ccd3a1726 --- /dev/null +++ b/eng/common/native/init-compiler.sh @@ -0,0 +1,137 @@ +#!/bin/sh +# +# This file detects the C/C++ compiler and exports it to the CC/CXX environment variables +# +# NOTE: some scripts source this file and rely on stdout being empty, make sure to not output anything here! + +if [ -z "$build_arch" ] || [ -z "$compiler" ]; then + echo "Usage..." + echo "build_arch= compiler= init-compiler.sh" + echo "Specify the target architecture." + echo "Specify the name of compiler (clang or gcc)." + exit 1 +fi + +case "$compiler" in + clang*|-clang*|--clang*) + # clangx.y or clang-x.y + version="$(echo "$compiler" | tr -d '[:alpha:]-=')" + majorVersion="${version%%.*}" + [ -z "${version##*.*}" ] && minorVersion="${version#*.}" + + if [ -z "$minorVersion" ] && [ -n "$majorVersion" ] && [ "$majorVersion" -le 6 ]; then + minorVersion=0; + fi + compiler=clang + ;; + + gcc*|-gcc*|--gcc*) + # gccx.y or gcc-x.y + version="$(echo "$compiler" | tr -d '[:alpha:]-=')" + majorVersion="${version%%.*}" + [ -z "${version##*.*}" ] && minorVersion="${version#*.}" + compiler=gcc + ;; +esac + +cxxCompiler="$compiler++" + +# clear the existing CC and CXX from environment +CC= +CXX= +LDFLAGS= + +if [ "$compiler" = "gcc" ]; then cxxCompiler="g++"; fi + +check_version_exists() { + desired_version=-1 + + # Set up the environment to be used for building with the desired compiler. + if command -v "$compiler-$1.$2" > /dev/null; then + desired_version="-$1.$2" + elif command -v "$compiler$1$2" > /dev/null; then + desired_version="$1$2" + elif command -v "$compiler-$1$2" > /dev/null; then + desired_version="-$1$2" + fi + + echo "$desired_version" +} + +if [ -z "$CLR_CC" ]; then + + # Set default versions + if [ -z "$majorVersion" ]; then + # note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero. + if [ "$compiler" = "clang" ]; then versions="18 17 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5" + elif [ "$compiler" = "gcc" ]; then versions="14 13 12 11 10 9 8 7 6 5 4.9"; fi + + for version in $versions; do + _major="${version%%.*}" + [ -z "${version##*.*}" ] && _minor="${version#*.}" + desired_version="$(check_version_exists "$_major" "$_minor")" + if [ "$desired_version" != "-1" ]; then majorVersion="$_major"; break; fi + done + + if [ -z "$majorVersion" ]; then + if command -v "$compiler" > /dev/null; then + if [ "$(uname)" != "Darwin" ]; then + echo "Warning: Specific version of $compiler not found, falling back to use the one in PATH." + fi + CC="$(command -v "$compiler")" + CXX="$(command -v "$cxxCompiler")" + else + echo "No usable version of $compiler found." + exit 1 + fi + else + if [ "$compiler" = "clang" ] && [ "$majorVersion" -lt 5 ]; then + if [ "$build_arch" = "arm" ] || [ "$build_arch" = "armel" ]; then + if command -v "$compiler" > /dev/null; then + echo "Warning: Found clang version $majorVersion which is not supported on arm/armel architectures, falling back to use clang from PATH." + CC="$(command -v "$compiler")" + CXX="$(command -v "$cxxCompiler")" + else + echo "Found clang version $majorVersion which is not supported on arm/armel architectures, and there is no clang in PATH." + exit 1 + fi + fi + fi + fi + else + desired_version="$(check_version_exists "$majorVersion" "$minorVersion")" + if [ "$desired_version" = "-1" ]; then + echo "Could not find specific version of $compiler: $majorVersion $minorVersion." + exit 1 + fi + fi + + if [ -z "$CC" ]; then + CC="$(command -v "$compiler$desired_version")" + CXX="$(command -v "$cxxCompiler$desired_version")" + if [ -z "$CXX" ]; then CXX="$(command -v "$cxxCompiler")"; fi + fi +else + if [ ! -f "$CLR_CC" ]; then + echo "CLR_CC is set but path '$CLR_CC' does not exist" + exit 1 + fi + CC="$CLR_CC" + CXX="$CLR_CXX" +fi + +if [ -z "$CC" ]; then + echo "Unable to find $compiler." + exit 1 +fi + +# Only lld version >= 9 can be considered stable. lld supports s390x starting from 18.0. +if [ "$compiler" = "clang" ] && [ -n "$majorVersion" ] && [ "$majorVersion" -ge 9 ] && ([ "$build_arch" != "s390x" ] || [ "$majorVersion" -ge 18 ]); then + if "$CC" -fuse-ld=lld -Wl,--version >/dev/null 2>&1; then + LDFLAGS="-fuse-ld=lld" + fi +fi + +SCAN_BUILD_COMMAND="$(command -v "scan-build$desired_version")" + +export CC CXX LDFLAGS SCAN_BUILD_COMMAND diff --git a/eng/common/native/init-distro-rid.sh b/eng/common/native/init-distro-rid.sh new file mode 100644 index 000000000..83ea7aab0 --- /dev/null +++ b/eng/common/native/init-distro-rid.sh @@ -0,0 +1,110 @@ +#!/bin/sh + +# getNonPortableDistroRid +# +# Input: +# targetOs: (str) +# targetArch: (str) +# rootfsDir: (str) +# +# Return: +# non-portable rid +getNonPortableDistroRid() +{ + targetOs="$1" + targetArch="$2" + rootfsDir="$3" + nonPortableRid="" + + if [ "$targetOs" = "linux" ]; then + # shellcheck disable=SC1091 + if [ -e "${rootfsDir}/etc/os-release" ]; then + . "${rootfsDir}/etc/os-release" + if echo "${VERSION_ID:-}" | grep -qE '^([[:digit:]]|\.)+$'; then + nonPortableRid="${ID}.${VERSION_ID}-${targetArch}" + else + # Rolling release distros either do not set VERSION_ID, set it as blank or + # set it to non-version looking string (such as TEMPLATE_VERSION_ID on ArchLinux); + # so omit it here to be consistent with everything else. + nonPortableRid="${ID}-${targetArch}" + fi + elif [ -e "${rootfsDir}/android_platform" ]; then + # shellcheck disable=SC1091 + . "${rootfsDir}/android_platform" + nonPortableRid="$RID" + fi + fi + + if [ "$targetOs" = "freebsd" ]; then + # $rootfsDir can be empty. freebsd-version is a shell script and should always work. + __freebsd_major_version=$("$rootfsDir"/bin/freebsd-version | cut -d'.' -f1) + nonPortableRid="freebsd.$__freebsd_major_version-${targetArch}" + elif command -v getprop >/dev/null && getprop ro.product.system.model | grep -qi android; then + __android_sdk_version=$(getprop ro.build.version.sdk) + nonPortableRid="android.$__android_sdk_version-${targetArch}" + elif [ "$targetOs" = "illumos" ]; then + __uname_version=$(uname -v) + nonPortableRid="illumos-${targetArch}" + elif [ "$targetOs" = "solaris" ]; then + __uname_version=$(uname -v) + __solaris_major_version=$(echo "$__uname_version" | cut -d'.' -f1) + nonPortableRid="solaris.$__solaris_major_version-${targetArch}" + elif [ "$targetOs" = "haiku" ]; then + __uname_release="$(uname -r)" + nonPortableRid=haiku.r"$__uname_release"-"$targetArch" + fi + + echo "$nonPortableRid" | tr '[:upper:]' '[:lower:]' +} + +# initDistroRidGlobal +# +# Input: +# os: (str) +# arch: (str) +# rootfsDir?: (nullable:string) +# +# Return: +# None +# +# Notes: +# It is important to note that the function does not return anything, but it +# exports the following variables on success: +# __DistroRid : Non-portable rid of the target platform. +# __PortableTargetOS : OS-part of the portable rid that corresponds to the target platform. +initDistroRidGlobal() +{ + targetOs="$1" + targetArch="$2" + rootfsDir="" + if [ $# -ge 3 ]; then + rootfsDir="$3" + fi + + if [ -n "${rootfsDir}" ]; then + # We may have a cross build. Check for the existence of the rootfsDir + if [ ! -e "${rootfsDir}" ]; then + echo "Error: rootfsDir has been passed, but the location is not valid." + exit 1 + fi + fi + + __DistroRid=$(getNonPortableDistroRid "${targetOs}" "${targetArch}" "${rootfsDir}") + + if [ -z "${__PortableTargetOS:-}" ]; then + __PortableTargetOS="$targetOs" + + STRINGS="$(command -v strings || true)" + if [ -z "$STRINGS" ]; then + STRINGS="$(command -v llvm-strings || true)" + fi + + # Check for musl-based distros (e.g. Alpine Linux, Void Linux). + if "${rootfsDir}/usr/bin/ldd" --version 2>&1 | grep -q musl || + ( [ -n "$STRINGS" ] && "$STRINGS" "${rootfsDir}/usr/bin/ldd" 2>&1 | grep -q musl ); then + __PortableTargetOS="linux-musl" + fi + fi + + export __DistroRid __PortableTargetOS +} diff --git a/eng/common/native/init-os-and-arch.sh b/eng/common/native/init-os-and-arch.sh new file mode 100644 index 000000000..38921d433 --- /dev/null +++ b/eng/common/native/init-os-and-arch.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +# Use uname to determine what the OS is. +OSName=$(uname -s | tr '[:upper:]' '[:lower:]') + +if command -v getprop && getprop ro.product.system.model 2>&1 | grep -qi android; then + OSName="android" +fi + +case "$OSName" in +freebsd|linux|netbsd|openbsd|sunos|android|haiku) + os="$OSName" ;; +darwin) + os=osx ;; +*) + echo "Unsupported OS $OSName detected!" + exit 1 ;; +esac + +# On Solaris, `uname -m` is discouraged, see https://docs.oracle.com/cd/E36784_01/html/E36870/uname-1.html +# and `uname -p` returns processor type (e.g. i386 on amd64). +# The appropriate tool to determine CPU is isainfo(1) https://docs.oracle.com/cd/E36784_01/html/E36870/isainfo-1.html. +if [ "$os" = "sunos" ]; then + if uname -o 2>&1 | grep -q illumos; then + os="illumos" + else + os="solaris" + fi + CPUName=$(isainfo -n) +else + # For the rest of the operating systems, use uname(1) to determine what the CPU is. + CPUName=$(uname -m) +fi + +case "$CPUName" in + arm64|aarch64) + arch=arm64 + if [ "$(getconf LONG_BIT)" -lt 64 ]; then + # This is 32-bit OS running on 64-bit CPU (for example Raspberry Pi OS) + arch=arm + fi + ;; + + loongarch64) + arch=loongarch64 + ;; + + riscv64) + arch=riscv64 + ;; + + amd64|x86_64) + arch=x64 + ;; + + armv7l|armv8l) + # shellcheck disable=SC1091 + if (NAME=""; . /etc/os-release; test "$NAME" = "Tizen"); then + arch=armel + else + arch=arm + fi + ;; + + armv6l) + arch=armv6 + ;; + + i[3-6]86) + echo "Unsupported CPU $CPUName detected, build might not succeed!" + arch=x86 + ;; + + s390x) + arch=s390x + ;; + + ppc64le) + arch=ppc64le + ;; + *) + echo "Unknown CPU $CPUName detected!" + exit 1 + ;; +esac diff --git a/eng/common/native/install-cmake-test.sh b/eng/common/native/install-cmake-test.sh new file mode 100755 index 000000000..8a5e7cf0d --- /dev/null +++ b/eng/common/native/install-cmake-test.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +. $scriptroot/common-library.sh + +base_uri= +install_path= +version= +clean=false +force=false +download_retries=5 +retry_wait_time_seconds=30 + +while (($# > 0)); do + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" + case $lowerI in + --baseuri) + base_uri=$2 + shift 2 + ;; + --installpath) + install_path=$2 + shift 2 + ;; + --version) + version=$2 + shift 2 + ;; + --clean) + clean=true + shift 1 + ;; + --force) + force=true + shift 1 + ;; + --downloadretries) + download_retries=$2 + shift 2 + ;; + --retrywaittimeseconds) + retry_wait_time_seconds=$2 + shift 2 + ;; + --help) + echo "Common settings:" + echo " --baseuri Base file directory or Url wrom which to acquire tool archives" + echo " --installpath Base directory to install native tool to" + echo " --clean Don't install the tool, just clean up the current install of the tool" + echo " --force Force install of tools even if they previously exist" + echo " --help Print help and exit" + echo "" + echo "Advanced settings:" + echo " --downloadretries Total number of retry attempts" + echo " --retrywaittimeseconds Wait time between retry attempts in seconds" + echo "" + exit 0 + ;; + esac +done + +tool_name="cmake-test" +tool_os=$(GetCurrentOS) +tool_folder="$(echo $tool_os | tr "[:upper:]" "[:lower:]")" +tool_arch="x86_64" +tool_name_moniker="$tool_name-$version-$tool_os-$tool_arch" +tool_install_directory="$install_path/$tool_name/$version" +tool_file_path="$tool_install_directory/$tool_name_moniker/bin/$tool_name" +shim_path="$install_path/$tool_name.sh" +uri="${base_uri}/$tool_folder/$tool_name/$tool_name_moniker.tar.gz" + +# Clean up tool and installers +if [[ $clean = true ]]; then + echo "Cleaning $tool_install_directory" + if [[ -d $tool_install_directory ]]; then + rm -rf $tool_install_directory + fi + + echo "Cleaning $shim_path" + if [[ -f $shim_path ]]; then + rm -rf $shim_path + fi + + tool_temp_path=$(GetTempPathFileName $uri) + echo "Cleaning $tool_temp_path" + if [[ -f $tool_temp_path ]]; then + rm -rf $tool_temp_path + fi + + exit 0 +fi + +# Install tool +if [[ -f $tool_file_path ]] && [[ $force = false ]]; then + echo "$tool_name ($version) already exists, skipping install" + exit 0 +fi + +DownloadAndExtract $uri $tool_install_directory $force $download_retries $retry_wait_time_seconds + +if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' 'Installation failed' + exit 1 +fi + +# Generate Shim +# Always rewrite shims so that we are referencing the expected version +NewScriptShim $shim_path $tool_file_path true + +if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' 'Shim generation failed' + exit 1 +fi + +exit 0 diff --git a/eng/common/native/install-cmake.sh b/eng/common/native/install-cmake.sh new file mode 100755 index 000000000..de496beeb --- /dev/null +++ b/eng/common/native/install-cmake.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +. $scriptroot/common-library.sh + +base_uri= +install_path= +version= +clean=false +force=false +download_retries=5 +retry_wait_time_seconds=30 + +while (($# > 0)); do + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" + case $lowerI in + --baseuri) + base_uri=$2 + shift 2 + ;; + --installpath) + install_path=$2 + shift 2 + ;; + --version) + version=$2 + shift 2 + ;; + --clean) + clean=true + shift 1 + ;; + --force) + force=true + shift 1 + ;; + --downloadretries) + download_retries=$2 + shift 2 + ;; + --retrywaittimeseconds) + retry_wait_time_seconds=$2 + shift 2 + ;; + --help) + echo "Common settings:" + echo " --baseuri Base file directory or Url wrom which to acquire tool archives" + echo " --installpath Base directory to install native tool to" + echo " --clean Don't install the tool, just clean up the current install of the tool" + echo " --force Force install of tools even if they previously exist" + echo " --help Print help and exit" + echo "" + echo "Advanced settings:" + echo " --downloadretries Total number of retry attempts" + echo " --retrywaittimeseconds Wait time between retry attempts in seconds" + echo "" + exit 0 + ;; + esac +done + +tool_name="cmake" +tool_os=$(GetCurrentOS) +tool_folder="$(echo $tool_os | tr "[:upper:]" "[:lower:]")" +tool_arch="x86_64" +tool_name_moniker="$tool_name-$version-$tool_os-$tool_arch" +tool_install_directory="$install_path/$tool_name/$version" +tool_file_path="$tool_install_directory/$tool_name_moniker/bin/$tool_name" +shim_path="$install_path/$tool_name.sh" +uri="${base_uri}/$tool_folder/$tool_name/$tool_name_moniker.tar.gz" + +# Clean up tool and installers +if [[ $clean = true ]]; then + echo "Cleaning $tool_install_directory" + if [[ -d $tool_install_directory ]]; then + rm -rf $tool_install_directory + fi + + echo "Cleaning $shim_path" + if [[ -f $shim_path ]]; then + rm -rf $shim_path + fi + + tool_temp_path=$(GetTempPathFileName $uri) + echo "Cleaning $tool_temp_path" + if [[ -f $tool_temp_path ]]; then + rm -rf $tool_temp_path + fi + + exit 0 +fi + +# Install tool +if [[ -f $tool_file_path ]] && [[ $force = false ]]; then + echo "$tool_name ($version) already exists, skipping install" + exit 0 +fi + +DownloadAndExtract $uri $tool_install_directory $force $download_retries $retry_wait_time_seconds + +if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' 'Installation failed' + exit 1 +fi + +# Generate Shim +# Always rewrite shims so that we are referencing the expected version +NewScriptShim $shim_path $tool_file_path true + +if [[ $? != 0 ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' 'Shim generation failed' + exit 1 +fi + +exit 0 diff --git a/eng/common/native/install-tool.ps1 b/eng/common/native/install-tool.ps1 new file mode 100644 index 000000000..78f2d84a4 --- /dev/null +++ b/eng/common/native/install-tool.ps1 @@ -0,0 +1,132 @@ +<# +.SYNOPSIS +Install native tool + +.DESCRIPTION +Install cmake native tool from Azure blob storage + +.PARAMETER InstallPath +Base directory to install native tool to + +.PARAMETER BaseUri +Base file directory or Url from which to acquire tool archives + +.PARAMETER CommonLibraryDirectory +Path to folder containing common library modules + +.PARAMETER Force +Force install of tools even if they previously exist + +.PARAMETER Clean +Don't install the tool, just clean up the current install of the tool + +.PARAMETER DownloadRetries +Total number of retry attempts + +.PARAMETER RetryWaitTimeInSeconds +Wait time between retry attempts in seconds + +.NOTES +Returns 0 if install succeeds, 1 otherwise +#> +[CmdletBinding(PositionalBinding=$false)] +Param ( + [Parameter(Mandatory=$True)] + [string] $ToolName, + [Parameter(Mandatory=$True)] + [string] $InstallPath, + [Parameter(Mandatory=$True)] + [string] $BaseUri, + [Parameter(Mandatory=$True)] + [string] $Version, + [string] $CommonLibraryDirectory = $PSScriptRoot, + [switch] $Force = $False, + [switch] $Clean = $False, + [int] $DownloadRetries = 5, + [int] $RetryWaitTimeInSeconds = 30 +) + +. $PSScriptRoot\..\pipeline-logging-functions.ps1 + +# Import common library modules +Import-Module -Name (Join-Path $CommonLibraryDirectory "CommonLibrary.psm1") + +try { + # Define verbose switch if undefined + $Verbose = $VerbosePreference -Eq "Continue" + + $Arch = CommonLibrary\Get-MachineArchitecture + $ToolOs = "win64" + if($Arch -Eq "x32") { + $ToolOs = "win32" + } + $ToolNameMoniker = "$ToolName-$Version-$ToolOs-$Arch" + $ToolInstallDirectory = Join-Path $InstallPath "$ToolName\$Version\" + $Uri = "$BaseUri/windows/$ToolName/$ToolNameMoniker.zip" + $ShimPath = Join-Path $InstallPath "$ToolName.exe" + + if ($Clean) { + Write-Host "Cleaning $ToolInstallDirectory" + if (Test-Path $ToolInstallDirectory) { + Remove-Item $ToolInstallDirectory -Force -Recurse + } + Write-Host "Cleaning $ShimPath" + if (Test-Path $ShimPath) { + Remove-Item $ShimPath -Force + } + $ToolTempPath = CommonLibrary\Get-TempPathFilename -Path $Uri + Write-Host "Cleaning $ToolTempPath" + if (Test-Path $ToolTempPath) { + Remove-Item $ToolTempPath -Force + } + exit 0 + } + + # Install tool + if ((Test-Path $ToolInstallDirectory) -And (-Not $Force)) { + Write-Verbose "$ToolName ($Version) already exists, skipping install" + } + else { + $InstallStatus = CommonLibrary\DownloadAndExtract -Uri $Uri ` + -InstallDirectory $ToolInstallDirectory ` + -Force:$Force ` + -DownloadRetries $DownloadRetries ` + -RetryWaitTimeInSeconds $RetryWaitTimeInSeconds ` + -Verbose:$Verbose + + if ($InstallStatus -Eq $False) { + Write-PipelineTelemetryError "Installation failed" -Category "NativeToolsetBootstrapping" + exit 1 + } + } + + $ToolFilePath = Get-ChildItem $ToolInstallDirectory -Recurse -Filter "$ToolName.exe" | % { $_.FullName } + if (@($ToolFilePath).Length -Gt 1) { + Write-Error "There are multiple copies of $ToolName in $($ToolInstallDirectory): `n$(@($ToolFilePath | out-string))" + exit 1 + } elseif (@($ToolFilePath).Length -Lt 1) { + Write-Host "$ToolName was not found in $ToolInstallDirectory." + exit 1 + } + + # Generate shim + # Always rewrite shims so that we are referencing the expected version + $GenerateShimStatus = CommonLibrary\New-ScriptShim -ShimName $ToolName ` + -ShimDirectory $InstallPath ` + -ToolFilePath "$ToolFilePath" ` + -BaseUri $BaseUri ` + -Force:$Force ` + -Verbose:$Verbose + + if ($GenerateShimStatus -Eq $False) { + Write-PipelineTelemetryError "Generate shim failed" -Category "NativeToolsetBootstrapping" + return 1 + } + + exit 0 +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category "NativeToolsetBootstrapping" -Message $_ + exit 1 +} diff --git a/eng/common/pipeline-logging-functions.ps1 b/eng/common/pipeline-logging-functions.ps1 new file mode 100644 index 000000000..8e422c561 --- /dev/null +++ b/eng/common/pipeline-logging-functions.ps1 @@ -0,0 +1,260 @@ +# Source for this file was taken from https://github.com/microsoft/azure-pipelines-task-lib/blob/11c9439d4af17e6475d9fe058e6b2e03914d17e6/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 and modified. + +# NOTE: You should not be calling these method directly as they are likely to change. Instead you should be calling the Write-Pipeline* functions defined in tools.ps1 + +$script:loggingCommandPrefix = '##vso[' +$script:loggingCommandEscapeMappings = @( # TODO: WHAT ABOUT "="? WHAT ABOUT "%"? + New-Object psobject -Property @{ Token = ';' ; Replacement = '%3B' } + New-Object psobject -Property @{ Token = "`r" ; Replacement = '%0D' } + New-Object psobject -Property @{ Token = "`n" ; Replacement = '%0A' } + New-Object psobject -Property @{ Token = "]" ; Replacement = '%5D' } +) +# TODO: BUG: Escape % ??? +# TODO: Add test to verify don't need to escape "=". + +# Specify "-Force" to force pipeline formatted output even if "$ci" is false or not set +function Write-PipelineTelemetryError { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Category, + [Parameter(Mandatory = $true)] + [string]$Message, + [Parameter(Mandatory = $false)] + [string]$Type = 'error', + [string]$ErrCode, + [string]$SourcePath, + [string]$LineNumber, + [string]$ColumnNumber, + [switch]$AsOutput, + [switch]$Force) + + $PSBoundParameters.Remove('Category') | Out-Null + + if ($Force -Or ((Test-Path variable:ci) -And $ci)) { + $Message = "(NETCORE_ENGINEERING_TELEMETRY=$Category) $Message" + } + $PSBoundParameters.Remove('Message') | Out-Null + $PSBoundParameters.Add('Message', $Message) + Write-PipelineTaskError @PSBoundParameters +} + +# Specify "-Force" to force pipeline formatted output even if "$ci" is false or not set +function Write-PipelineTaskError { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Message, + [Parameter(Mandatory = $false)] + [string]$Type = 'error', + [string]$ErrCode, + [string]$SourcePath, + [string]$LineNumber, + [string]$ColumnNumber, + [switch]$AsOutput, + [switch]$Force + ) + + if (!$Force -And (-Not (Test-Path variable:ci) -Or !$ci)) { + if ($Type -eq 'error') { + Write-Host $Message -ForegroundColor Red + return + } + elseif ($Type -eq 'warning') { + Write-Host $Message -ForegroundColor Yellow + return + } + } + + if (($Type -ne 'error') -and ($Type -ne 'warning')) { + Write-Host $Message + return + } + $PSBoundParameters.Remove('Force') | Out-Null + if (-not $PSBoundParameters.ContainsKey('Type')) { + $PSBoundParameters.Add('Type', 'error') + } + Write-LogIssue @PSBoundParameters +} + +function Write-PipelineSetVariable { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Name, + [string]$Value, + [switch]$Secret, + [switch]$AsOutput, + [bool]$IsMultiJobVariable = $true) + + if ((Test-Path variable:ci) -And $ci) { + Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data $Value -Properties @{ + 'variable' = $Name + 'isSecret' = $Secret + 'isOutput' = $IsMultiJobVariable + } -AsOutput:$AsOutput + } +} + +function Write-PipelinePrependPath { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Path, + [switch]$AsOutput) + + if ((Test-Path variable:ci) -And $ci) { + Write-LoggingCommand -Area 'task' -Event 'prependpath' -Data $Path -AsOutput:$AsOutput + } +} + +function Write-PipelineSetResult { + [CmdletBinding()] + param( + [ValidateSet("Succeeded", "SucceededWithIssues", "Failed", "Cancelled", "Skipped")] + [Parameter(Mandatory = $true)] + [string]$Result, + [string]$Message) + if ((Test-Path variable:ci) -And $ci) { + Write-LoggingCommand -Area 'task' -Event 'complete' -Data $Message -Properties @{ + 'result' = $Result + } + } +} + +<######################################## +# Private functions. +########################################> +function Format-LoggingCommandData { + [CmdletBinding()] + param([string]$Value, [switch]$Reverse) + + if (!$Value) { + return '' + } + + if (!$Reverse) { + foreach ($mapping in $script:loggingCommandEscapeMappings) { + $Value = $Value.Replace($mapping.Token, $mapping.Replacement) + } + } + else { + for ($i = $script:loggingCommandEscapeMappings.Length - 1 ; $i -ge 0 ; $i--) { + $mapping = $script:loggingCommandEscapeMappings[$i] + $Value = $Value.Replace($mapping.Replacement, $mapping.Token) + } + } + + return $Value +} + +function Format-LoggingCommand { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Area, + [Parameter(Mandatory = $true)] + [string]$Event, + [string]$Data, + [hashtable]$Properties) + + # Append the preamble. + [System.Text.StringBuilder]$sb = New-Object -TypeName System.Text.StringBuilder + $null = $sb.Append($script:loggingCommandPrefix).Append($Area).Append('.').Append($Event) + + # Append the properties. + if ($Properties) { + $first = $true + foreach ($key in $Properties.Keys) { + [string]$value = Format-LoggingCommandData $Properties[$key] + if ($value) { + if ($first) { + $null = $sb.Append(' ') + $first = $false + } + else { + $null = $sb.Append(';') + } + + $null = $sb.Append("$key=$value") + } + } + } + + # Append the tail and output the value. + $Data = Format-LoggingCommandData $Data + $sb.Append(']').Append($Data).ToString() +} + +function Write-LoggingCommand { + [CmdletBinding(DefaultParameterSetName = 'Parameters')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Parameters')] + [string]$Area, + [Parameter(Mandatory = $true, ParameterSetName = 'Parameters')] + [string]$Event, + [Parameter(ParameterSetName = 'Parameters')] + [string]$Data, + [Parameter(ParameterSetName = 'Parameters')] + [hashtable]$Properties, + [Parameter(Mandatory = $true, ParameterSetName = 'Object')] + $Command, + [switch]$AsOutput) + + if ($PSCmdlet.ParameterSetName -eq 'Object') { + Write-LoggingCommand -Area $Command.Area -Event $Command.Event -Data $Command.Data -Properties $Command.Properties -AsOutput:$AsOutput + return + } + + $command = Format-LoggingCommand -Area $Area -Event $Event -Data $Data -Properties $Properties + if ($AsOutput) { + $command + } + else { + Write-Host $command + } +} + +function Write-LogIssue { + [CmdletBinding()] + param( + [ValidateSet('warning', 'error')] + [Parameter(Mandatory = $true)] + [string]$Type, + [string]$Message, + [string]$ErrCode, + [string]$SourcePath, + [string]$LineNumber, + [string]$ColumnNumber, + [switch]$AsOutput) + + $command = Format-LoggingCommand -Area 'task' -Event 'logissue' -Data $Message -Properties @{ + 'type' = $Type + 'code' = $ErrCode + 'sourcepath' = $SourcePath + 'linenumber' = $LineNumber + 'columnnumber' = $ColumnNumber + } + if ($AsOutput) { + return $command + } + + if ($Type -eq 'error') { + $foregroundColor = $host.PrivateData.ErrorForegroundColor + $backgroundColor = $host.PrivateData.ErrorBackgroundColor + if ($foregroundColor -isnot [System.ConsoleColor] -or $backgroundColor -isnot [System.ConsoleColor]) { + $foregroundColor = [System.ConsoleColor]::Red + $backgroundColor = [System.ConsoleColor]::Black + } + } + else { + $foregroundColor = $host.PrivateData.WarningForegroundColor + $backgroundColor = $host.PrivateData.WarningBackgroundColor + if ($foregroundColor -isnot [System.ConsoleColor] -or $backgroundColor -isnot [System.ConsoleColor]) { + $foregroundColor = [System.ConsoleColor]::Yellow + $backgroundColor = [System.ConsoleColor]::Black + } + } + + Write-Host $command -ForegroundColor $foregroundColor -BackgroundColor $backgroundColor +} diff --git a/eng/common/pipeline-logging-functions.sh b/eng/common/pipeline-logging-functions.sh new file mode 100755 index 000000000..6a0b2255e --- /dev/null +++ b/eng/common/pipeline-logging-functions.sh @@ -0,0 +1,206 @@ +#!/usr/bin/env bash + +function Write-PipelineTelemetryError { + local telemetry_category='' + local force=false + local function_args=() + local message='' + while [[ $# -gt 0 ]]; do + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -category|-c) + telemetry_category=$2 + shift + ;; + -force|-f) + force=true + ;; + -*) + function_args+=("$1 $2") + shift + ;; + *) + message=$* + ;; + esac + shift + done + + if [[ $force != true ]] && [[ "$ci" != true ]]; then + echo "$message" >&2 + return + fi + + if [[ $force == true ]]; then + function_args+=("-force") + fi + message="(NETCORE_ENGINEERING_TELEMETRY=$telemetry_category) $message" + function_args+=("$message") + Write-PipelineTaskError ${function_args[@]} +} + +function Write-PipelineTaskError { + local message_type="error" + local sourcepath='' + local linenumber='' + local columnnumber='' + local error_code='' + local force=false + + while [[ $# -gt 0 ]]; do + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -type|-t) + message_type=$2 + shift + ;; + -sourcepath|-s) + sourcepath=$2 + shift + ;; + -linenumber|-ln) + linenumber=$2 + shift + ;; + -columnnumber|-cn) + columnnumber=$2 + shift + ;; + -errcode|-e) + error_code=$2 + shift + ;; + -force|-f) + force=true + ;; + *) + break + ;; + esac + + shift + done + + if [[ $force != true ]] && [[ "$ci" != true ]]; then + echo "$@" >&2 + return + fi + + local message="##vso[task.logissue" + + message="$message type=$message_type" + + if [ -n "$sourcepath" ]; then + message="$message;sourcepath=$sourcepath" + fi + + if [ -n "$linenumber" ]; then + message="$message;linenumber=$linenumber" + fi + + if [ -n "$columnnumber" ]; then + message="$message;columnnumber=$columnnumber" + fi + + if [ -n "$error_code" ]; then + message="$message;code=$error_code" + fi + + message="$message]$*" + echo "$message" +} + +function Write-PipelineSetVariable { + if [[ "$ci" != true ]]; then + return + fi + + local name='' + local value='' + local secret=false + local as_output=false + local is_multi_job_variable=true + + while [[ $# -gt 0 ]]; do + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -name|-n) + name=$2 + shift + ;; + -value|-v) + value=$2 + shift + ;; + -secret|-s) + secret=true + ;; + -as_output|-a) + as_output=true + ;; + -is_multi_job_variable|-i) + is_multi_job_variable=$2 + shift + ;; + esac + shift + done + + value=${value/;/%3B} + value=${value/\\r/%0D} + value=${value/\\n/%0A} + value=${value/]/%5D} + + local message="##vso[task.setvariable variable=$name;isSecret=$secret;isOutput=$is_multi_job_variable]$value" + + if [[ "$as_output" == true ]]; then + $message + else + echo "$message" + fi +} + +function Write-PipelinePrependPath { + local prepend_path='' + + while [[ $# -gt 0 ]]; do + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -path|-p) + prepend_path=$2 + shift + ;; + esac + shift + done + + export PATH="$prepend_path:$PATH" + + if [[ "$ci" == true ]]; then + echo "##vso[task.prependpath]$prepend_path" + fi +} + +function Write-PipelineSetResult { + local result='' + local message='' + + while [[ $# -gt 0 ]]; do + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -result|-r) + result=$2 + shift + ;; + -message|-m) + message=$2 + shift + ;; + esac + shift + done + + if [[ "$ci" == true ]]; then + echo "##vso[task.complete result=$result;]$message" + fi +} diff --git a/eng/common/post-build/add-build-to-channel.ps1 b/eng/common/post-build/add-build-to-channel.ps1 new file mode 100644 index 000000000..49938f0c8 --- /dev/null +++ b/eng/common/post-build/add-build-to-channel.ps1 @@ -0,0 +1,48 @@ +param( + [Parameter(Mandatory=$true)][int] $BuildId, + [Parameter(Mandatory=$true)][int] $ChannelId, + [Parameter(Mandatory=$true)][string] $MaestroApiAccessToken, + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', + [Parameter(Mandatory=$false)][string] $MaestroApiVersion = '2019-01-16' +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + # Check that the channel we are going to promote the build to exist + $channelInfo = Get-MaestroChannel -ChannelId $ChannelId + + if (!$channelInfo) { + Write-PipelineTelemetryCategory -Category 'PromoteBuild' -Message "Channel with BAR ID $ChannelId was not found in BAR!" + ExitWithExitCode 1 + } + + # Get info about which channel(s) the build has already been promoted to + $buildInfo = Get-MaestroBuild -BuildId $BuildId + + if (!$buildInfo) { + Write-PipelineTelemetryError -Category 'PromoteBuild' -Message "Build with BAR ID $BuildId was not found in BAR!" + ExitWithExitCode 1 + } + + # Find whether the build is already assigned to the channel or not + if ($buildInfo.channels) { + foreach ($channel in $buildInfo.channels) { + if ($channel.Id -eq $ChannelId) { + Write-Host "The build with BAR ID $BuildId is already on channel $ChannelId!" + ExitWithExitCode 0 + } + } + } + + Write-Host "Promoting build '$BuildId' to channel '$ChannelId'." + + Assign-BuildToChannel -BuildId $BuildId -ChannelId $ChannelId + + Write-Host 'done.' +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Category 'PromoteBuild' -Message "There was an error while trying to promote build '$BuildId' to channel '$ChannelId'" + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/check-channel-consistency.ps1 b/eng/common/post-build/check-channel-consistency.ps1 new file mode 100644 index 000000000..1728f035a --- /dev/null +++ b/eng/common/post-build/check-channel-consistency.ps1 @@ -0,0 +1,40 @@ +param( + [Parameter(Mandatory=$true)][string] $PromoteToChannels, # List of channels that the build should be promoted to + [Parameter(Mandatory=$true)][array] $AvailableChannelIds # List of channel IDs available in the YAML implementation +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + if ($PromoteToChannels -eq "") { + Write-PipelineTaskError -Type 'warning' -Message "This build won't publish assets as it's not configured to any Maestro channel. If that wasn't intended use Darc to configure a default channel using add-default-channel for this branch or to promote it to a channel using add-build-to-channel. See https://github.com/dotnet/arcade/blob/main/Documentation/Darc.md#assigning-an-individual-build-to-a-channel for more info." + ExitWithExitCode 0 + } + + # Check that every channel that Maestro told to promote the build to + # is available in YAML + $PromoteToChannelsIds = $PromoteToChannels -split "\D" | Where-Object { $_ } + + $hasErrors = $false + + foreach ($id in $PromoteToChannelsIds) { + if (($id -ne 0) -and ($id -notin $AvailableChannelIds)) { + Write-PipelineTaskError -Message "Channel $id is not present in the post-build YAML configuration! This is an error scenario. Please contact @dnceng." + $hasErrors = $true + } + } + + # The `Write-PipelineTaskError` doesn't error the script and we might report several errors + # in the previous lines. The check below makes sure that we return an error state from the + # script if we reported any validation error + if ($hasErrors) { + ExitWithExitCode 1 + } + + Write-Host 'done.' +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Category 'CheckChannelConsistency' -Message "There was an error while trying to check consistency of Maestro default channels for the build and post-build YAML configuration." + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/nuget-validation.ps1 b/eng/common/post-build/nuget-validation.ps1 new file mode 100644 index 000000000..dab3534ab --- /dev/null +++ b/eng/common/post-build/nuget-validation.ps1 @@ -0,0 +1,24 @@ +# This script validates NuGet package metadata information using this +# tool: https://github.com/NuGet/NuGetGallery/tree/jver-verify/src/VerifyMicrosoftPackage + +param( + [Parameter(Mandatory=$true)][string] $PackagesPath, # Path to where the packages to be validated are + [Parameter(Mandatory=$true)][string] $ToolDestinationPath # Where the validation tool should be downloaded to +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + $url = 'https://raw.githubusercontent.com/NuGet/NuGetGallery/3e25ad135146676bcab0050a516939d9958bfa5d/src/VerifyMicrosoftPackage/verify.ps1' + + New-Item -ItemType 'directory' -Path ${ToolDestinationPath} -Force + + Invoke-WebRequest $url -OutFile ${ToolDestinationPath}\verify.ps1 + + & ${ToolDestinationPath}\verify.ps1 ${PackagesPath}\*.nupkg +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'NuGetValidation' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/post-build-utils.ps1 b/eng/common/post-build/post-build-utils.ps1 new file mode 100644 index 000000000..534f6988d --- /dev/null +++ b/eng/common/post-build/post-build-utils.ps1 @@ -0,0 +1,91 @@ +# Most of the functions in this file require the variables `MaestroApiEndPoint`, +# `MaestroApiVersion` and `MaestroApiAccessToken` to be globally available. + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 + +# `tools.ps1` checks $ci to perform some actions. Since the post-build +# scripts don't necessarily execute in the same agent that run the +# build.ps1/sh script this variable isn't automatically set. +$ci = $true +$disableConfigureToolsetImport = $true +. $PSScriptRoot\..\tools.ps1 + +function Create-MaestroApiRequestHeaders([string]$ContentType = 'application/json') { + Validate-MaestroVars + + $headers = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' + $headers.Add('Accept', $ContentType) + $headers.Add('Authorization',"Bearer $MaestroApiAccessToken") + return $headers +} + +function Get-MaestroChannel([int]$ChannelId) { + Validate-MaestroVars + + $apiHeaders = Create-MaestroApiRequestHeaders + $apiEndpoint = "$MaestroApiEndPoint/api/channels/${ChannelId}?api-version=$MaestroApiVersion" + + $result = try { Invoke-WebRequest -Method Get -Uri $apiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } + return $result +} + +function Get-MaestroBuild([int]$BuildId) { + Validate-MaestroVars + + $apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken + $apiEndpoint = "$MaestroApiEndPoint/api/builds/${BuildId}?api-version=$MaestroApiVersion" + + $result = try { return Invoke-WebRequest -Method Get -Uri $apiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } + return $result +} + +function Get-MaestroSubscriptions([string]$SourceRepository, [int]$ChannelId) { + Validate-MaestroVars + + $SourceRepository = [System.Web.HttpUtility]::UrlEncode($SourceRepository) + $apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken + $apiEndpoint = "$MaestroApiEndPoint/api/subscriptions?sourceRepository=$SourceRepository&channelId=$ChannelId&api-version=$MaestroApiVersion" + + $result = try { Invoke-WebRequest -Method Get -Uri $apiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } + return $result +} + +function Assign-BuildToChannel([int]$BuildId, [int]$ChannelId) { + Validate-MaestroVars + + $apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken + $apiEndpoint = "$MaestroApiEndPoint/api/channels/${ChannelId}/builds/${BuildId}?api-version=$MaestroApiVersion" + Invoke-WebRequest -Method Post -Uri $apiEndpoint -Headers $apiHeaders | Out-Null +} + +function Trigger-Subscription([string]$SubscriptionId) { + Validate-MaestroVars + + $apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken + $apiEndpoint = "$MaestroApiEndPoint/api/subscriptions/$SubscriptionId/trigger?api-version=$MaestroApiVersion" + Invoke-WebRequest -Uri $apiEndpoint -Headers $apiHeaders -Method Post | Out-Null +} + +function Validate-MaestroVars { + try { + Get-Variable MaestroApiEndPoint | Out-Null + Get-Variable MaestroApiVersion | Out-Null + Get-Variable MaestroApiAccessToken | Out-Null + + if (!($MaestroApiEndPoint -Match '^http[s]?://maestro-(int|prod).westus2.cloudapp.azure.com$')) { + Write-PipelineTelemetryError -Category 'MaestroVars' -Message "MaestroApiEndPoint is not a valid Maestro URL. '$MaestroApiEndPoint'" + ExitWithExitCode 1 + } + + if (!($MaestroApiVersion -Match '^[0-9]{4}-[0-9]{2}-[0-9]{2}$')) { + Write-PipelineTelemetryError -Category 'MaestroVars' -Message "MaestroApiVersion does not match a version string in the format yyyy-MM-DD. '$MaestroApiVersion'" + ExitWithExitCode 1 + } + } + catch { + Write-PipelineTelemetryError -Category 'MaestroVars' -Message 'Error: Variables `MaestroApiEndPoint`, `MaestroApiVersion` and `MaestroApiAccessToken` are required while using this script.' + Write-Host $_ + ExitWithExitCode 1 + } +} diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 new file mode 100644 index 000000000..5a3a32ea8 --- /dev/null +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -0,0 +1,54 @@ +param( + [Parameter(Mandatory=$true)][int] $BuildId, + [Parameter(Mandatory=$true)][int] $PublishingInfraVersion, + [Parameter(Mandatory=$true)][string] $AzdoToken, + [Parameter(Mandatory=$true)][string] $MaestroToken, + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', + [Parameter(Mandatory=$true)][string] $WaitPublishingFinish, + [Parameter(Mandatory=$false)][string] $ArtifactsPublishingAdditionalParameters, + [Parameter(Mandatory=$false)][string] $SymbolPublishingAdditionalParameters +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + $darc = Get-Darc + + $optionalParams = [System.Collections.ArrayList]::new() + + if ("" -ne $ArtifactsPublishingAdditionalParameters) { + $optionalParams.Add("--artifact-publishing-parameters") | Out-Null + $optionalParams.Add($ArtifactsPublishingAdditionalParameters) | Out-Null + } + + if ("" -ne $SymbolPublishingAdditionalParameters) { + $optionalParams.Add("--symbol-publishing-parameters") | Out-Null + $optionalParams.Add($SymbolPublishingAdditionalParameters) | Out-Null + } + + if ("false" -eq $WaitPublishingFinish) { + $optionalParams.Add("--no-wait") | Out-Null + } + + & $darc add-build-to-channel ` + --id $buildId ` + --publishing-infra-version $PublishingInfraVersion ` + --default-channels ` + --source-branch main ` + --azdev-pat $AzdoToken ` + --bar-uri $MaestroApiEndPoint ` + --password $MaestroToken ` + @optionalParams + + if ($LastExitCode -ne 0) { + Write-Host "Problems using Darc to promote build ${buildId} to default channels. Stopping execution..." + exit 1 + } + + Write-Host 'done.' +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Category 'PromoteBuild' -Message "There was an error while trying to publish build '$BuildId' to default channels." + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/redact-logs.ps1 b/eng/common/post-build/redact-logs.ps1 new file mode 100644 index 000000000..82d91f6fd --- /dev/null +++ b/eng/common/post-build/redact-logs.ps1 @@ -0,0 +1,81 @@ +[CmdletBinding(PositionalBinding=$False)] +param( + [Parameter(Mandatory=$true, Position=0)][string] $InputPath, + [Parameter(Mandatory=$true)][string] $BinlogToolVersion, + [Parameter(Mandatory=$false)][string] $DotnetPath, + [Parameter(Mandatory=$false)][string] $PackageFeed = 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json', + # File with strings to redact - separated by newlines. + # For comments start the line with '# ' - such lines are ignored + [Parameter(Mandatory=$false)][string] $TokensFilePath, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$TokensToRedact +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + $packageName = 'binlogtool' + + $dotnet = $DotnetPath + + if (!$dotnet) { + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + } + + $toolList = & "$dotnet" tool list -g + + if ($toolList -like "*$packageName*") { + & "$dotnet" tool uninstall $packageName -g + } + + $toolPath = "$PSScriptRoot\..\..\..\.tools" + $verbosity = 'minimal' + + New-Item -ItemType Directory -Force -Path $toolPath + + Push-Location -Path $toolPath + + try { + Write-Host "Installing Binlog redactor CLI..." + Write-Host "'$dotnet' new tool-manifest" + & "$dotnet" new tool-manifest + Write-Host "'$dotnet' tool install $packageName --local --add-source '$PackageFeed' -v $verbosity --version $BinlogToolVersion" + & "$dotnet" tool install $packageName --local --add-source "$PackageFeed" -v $verbosity --version $BinlogToolVersion + + if (Test-Path $TokensFilePath) { + Write-Host "Adding additional sensitive data for redaction from file: " $TokensFilePath + $TokensToRedact += Get-Content -Path $TokensFilePath | Foreach {$_.Trim()} | Where { $_ -notmatch "^# " } + } + + $optionalParams = [System.Collections.ArrayList]::new() + + Foreach ($p in $TokensToRedact) + { + if($p -match '^\$\(.*\)$') + { + Write-Host ("Ignoring token {0} as it is probably unexpanded AzDO variable" -f $p) + } + elseif($p) + { + $optionalParams.Add("-p:" + $p) | Out-Null + } + } + + & $dotnet binlogtool redact --input:$InputPath --recurse --in-place ` + @optionalParams + + if ($LastExitCode -ne 0) { + Write-PipelineTelemetryError -Category 'Redactor' -Type 'warning' -Message "Problems using Redactor tool (exit code: $LastExitCode). But ignoring them now." + } + } + finally { + Pop-Location + } + + Write-Host 'done.' +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Category 'Redactor' -Message "There was an error while trying to redact logs. Error: $_" + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/sourcelink-validation.ps1 b/eng/common/post-build/sourcelink-validation.ps1 new file mode 100644 index 000000000..4011d324e --- /dev/null +++ b/eng/common/post-build/sourcelink-validation.ps1 @@ -0,0 +1,319 @@ +param( + [Parameter(Mandatory=$true)][string] $InputPath, # Full path to directory where Symbols.NuGet packages to be checked are stored + [Parameter(Mandatory=$true)][string] $ExtractPath, # Full path to directory where the packages will be extracted during validation + [Parameter(Mandatory=$false)][string] $GHRepoName, # GitHub name of the repo including the Org. E.g., dotnet/arcade + [Parameter(Mandatory=$false)][string] $GHCommit, # GitHub commit SHA used to build the packages + [Parameter(Mandatory=$true)][string] $SourcelinkCliVersion # Version of SourceLink CLI to use +) + +. $PSScriptRoot\post-build-utils.ps1 + +# Cache/HashMap (File -> Exist flag) used to consult whether a file exist +# in the repository at a specific commit point. This is populated by inserting +# all files present in the repo at a specific commit point. +$global:RepoFiles = @{} + +# Maximum number of jobs to run in parallel +$MaxParallelJobs = 16 + +$MaxRetries = 5 +$RetryWaitTimeInSeconds = 30 + +# Wait time between check for system load +$SecondsBetweenLoadChecks = 10 + +if (!$InputPath -or !(Test-Path $InputPath)){ + Write-Host "No files to validate." + ExitWithExitCode 0 +} + +$ValidatePackage = { + param( + [string] $PackagePath # Full path to a Symbols.NuGet package + ) + + . $using:PSScriptRoot\..\tools.ps1 + + # Ensure input file exist + if (!(Test-Path $PackagePath)) { + Write-Host "Input file does not exist: $PackagePath" + return [pscustomobject]@{ + result = 1 + packagePath = $PackagePath + } + } + + # Extensions for which we'll look for SourceLink information + # For now we'll only care about Portable & Embedded PDBs + $RelevantExtensions = @('.dll', '.exe', '.pdb') + + Write-Host -NoNewLine 'Validating ' ([System.IO.Path]::GetFileName($PackagePath)) '...' + + $PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath) + $ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageId + $FailedFiles = 0 + + Add-Type -AssemblyName System.IO.Compression.FileSystem + + [System.IO.Directory]::CreateDirectory($ExtractPath) | Out-Null + + try { + $zip = [System.IO.Compression.ZipFile]::OpenRead($PackagePath) + + $zip.Entries | + Where-Object {$RelevantExtensions -contains [System.IO.Path]::GetExtension($_.Name)} | + ForEach-Object { + $FileName = $_.FullName + $Extension = [System.IO.Path]::GetExtension($_.Name) + $FakeName = -Join((New-Guid), $Extension) + $TargetFile = Join-Path -Path $ExtractPath -ChildPath $FakeName + + # We ignore resource DLLs + if ($FileName.EndsWith('.resources.dll')) { + return [pscustomobject]@{ + result = 0 + packagePath = $PackagePath + } + } + + [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile, $true) + + $ValidateFile = { + param( + [string] $FullPath, # Full path to the module that has to be checked + [string] $RealPath, + [ref] $FailedFiles + ) + + $sourcelinkExe = "$env:USERPROFILE\.dotnet\tools" + $sourcelinkExe = Resolve-Path "$sourcelinkExe\sourcelink.exe" + $SourceLinkInfos = & $sourcelinkExe print-urls $FullPath | Out-String + + if ($LASTEXITCODE -eq 0 -and -not ([string]::IsNullOrEmpty($SourceLinkInfos))) { + $NumFailedLinks = 0 + + # We only care about Http addresses + $Matches = (Select-String '(http[s]?)(:\/\/)([^\s,]+)' -Input $SourceLinkInfos -AllMatches).Matches + + if ($Matches.Count -ne 0) { + $Matches.Value | + ForEach-Object { + $Link = $_ + $CommitUrl = "https://raw.githubusercontent.com/${using:GHRepoName}/${using:GHCommit}/" + + $FilePath = $Link.Replace($CommitUrl, "") + $Status = 200 + $Cache = $using:RepoFiles + + $attempts = 0 + + while ($attempts -lt $using:MaxRetries) { + if ( !($Cache.ContainsKey($FilePath)) ) { + try { + $Uri = $Link -as [System.URI] + + if ($Link -match "submodules") { + # Skip submodule links until sourcelink properly handles submodules + $Status = 200 + } + elseif ($Uri.AbsoluteURI -ne $null -and ($Uri.Host -match 'github' -or $Uri.Host -match 'githubusercontent')) { + # Only GitHub links are valid + $Status = (Invoke-WebRequest -Uri $Link -UseBasicParsing -Method HEAD -TimeoutSec 5).StatusCode + } + else { + # If it's not a github link, we want to break out of the loop and not retry. + $Status = 0 + $attempts = $using:MaxRetries + } + } + catch { + Write-Host $_ + $Status = 0 + } + } + + if ($Status -ne 200) { + $attempts++ + + if ($attempts -lt $using:MaxRetries) + { + $attemptsLeft = $using:MaxRetries - $attempts + Write-Warning "Download failed, $attemptsLeft attempts remaining, will retry in $using:RetryWaitTimeInSeconds seconds" + Start-Sleep -Seconds $using:RetryWaitTimeInSeconds + } + else { + if ($NumFailedLinks -eq 0) { + if ($FailedFiles.Value -eq 0) { + Write-Host + } + + Write-Host "`tFile $RealPath has broken links:" + } + + Write-Host "`t`tFailed to retrieve $Link" + + $NumFailedLinks++ + } + } + else { + break + } + } + } + } + + if ($NumFailedLinks -ne 0) { + $FailedFiles.value++ + $global:LASTEXITCODE = 1 + } + } + } + + &$ValidateFile $TargetFile $FileName ([ref]$FailedFiles) + } + } + catch { + Write-Host $_ + } + finally { + $zip.Dispose() + } + + if ($FailedFiles -eq 0) { + Write-Host 'Passed.' + return [pscustomobject]@{ + result = 0 + packagePath = $PackagePath + } + } + else { + Write-PipelineTelemetryError -Category 'SourceLink' -Message "$PackagePath has broken SourceLink links." + return [pscustomobject]@{ + result = 1 + packagePath = $PackagePath + } + } +} + +function CheckJobResult( + $result, + $packagePath, + [ref]$ValidationFailures, + [switch]$logErrors) { + if ($result -ne '0') { + if ($logErrors) { + Write-PipelineTelemetryError -Category 'SourceLink' -Message "$packagePath has broken SourceLink links." + } + $ValidationFailures.Value++ + } +} + +function ValidateSourceLinkLinks { + if ($GHRepoName -ne '' -and !($GHRepoName -Match '^[^\s\/]+/[^\s\/]+$')) { + if (!($GHRepoName -Match '^[^\s-]+-[^\s]+$')) { + Write-PipelineTelemetryError -Category 'SourceLink' -Message "GHRepoName should be in the format / or -. '$GHRepoName'" + ExitWithExitCode 1 + } + else { + $GHRepoName = $GHRepoName -replace '^([^\s-]+)-([^\s]+)$', '$1/$2'; + } + } + + if ($GHCommit -ne '' -and !($GHCommit -Match '^[0-9a-fA-F]{40}$')) { + Write-PipelineTelemetryError -Category 'SourceLink' -Message "GHCommit should be a 40 chars hexadecimal string. '$GHCommit'" + ExitWithExitCode 1 + } + + if ($GHRepoName -ne '' -and $GHCommit -ne '') { + $RepoTreeURL = -Join('http://api.github.com/repos/', $GHRepoName, '/git/trees/', $GHCommit, '?recursive=1') + $CodeExtensions = @('.cs', '.vb', '.fs', '.fsi', '.fsx', '.fsscript') + + try { + # Retrieve the list of files in the repo at that particular commit point and store them in the RepoFiles hash + $Data = Invoke-WebRequest $RepoTreeURL -UseBasicParsing | ConvertFrom-Json | Select-Object -ExpandProperty tree + + foreach ($file in $Data) { + $Extension = [System.IO.Path]::GetExtension($file.path) + + if ($CodeExtensions.Contains($Extension)) { + $RepoFiles[$file.path] = 1 + } + } + } + catch { + Write-Host "Problems downloading the list of files from the repo. Url used: $RepoTreeURL . Execution will proceed without caching." + } + } + elseif ($GHRepoName -ne '' -or $GHCommit -ne '') { + Write-Host 'For using the http caching mechanism both GHRepoName and GHCommit should be informed.' + } + + if (Test-Path $ExtractPath) { + Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue + } + + $ValidationFailures = 0 + + # Process each NuGet package in parallel + Get-ChildItem "$InputPath\*.symbols.nupkg" | + ForEach-Object { + Write-Host "Starting $($_.FullName)" + Start-Job -ScriptBlock $ValidatePackage -ArgumentList $_.FullName | Out-Null + $NumJobs = @(Get-Job -State 'Running').Count + + while ($NumJobs -ge $MaxParallelJobs) { + Write-Host "There are $NumJobs validation jobs running right now. Waiting $SecondsBetweenLoadChecks seconds to check again." + sleep $SecondsBetweenLoadChecks + $NumJobs = @(Get-Job -State 'Running').Count + } + + foreach ($Job in @(Get-Job -State 'Completed')) { + $jobResult = Wait-Job -Id $Job.Id | Receive-Job + CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$ValidationFailures) -LogErrors + Remove-Job -Id $Job.Id + } + } + + foreach ($Job in @(Get-Job)) { + $jobResult = Wait-Job -Id $Job.Id | Receive-Job + CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$ValidationFailures) + Remove-Job -Id $Job.Id + } + if ($ValidationFailures -gt 0) { + Write-PipelineTelemetryError -Category 'SourceLink' -Message "$ValidationFailures package(s) failed validation." + ExitWithExitCode 1 + } +} + +function InstallSourcelinkCli { + $sourcelinkCliPackageName = 'sourcelink' + + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + $toolList = & "$dotnet" tool list --global + + if (($toolList -like "*$sourcelinkCliPackageName*") -and ($toolList -like "*$sourcelinkCliVersion*")) { + Write-Host "SourceLink CLI version $sourcelinkCliVersion is already installed." + } + else { + Write-Host "Installing SourceLink CLI version $sourcelinkCliVersion..." + Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.' + & "$dotnet" tool install $sourcelinkCliPackageName --version $sourcelinkCliVersion --verbosity "minimal" --global + } +} + +try { + InstallSourcelinkCli + + foreach ($Job in @(Get-Job)) { + Remove-Job -Id $Job.Id + } + + ValidateSourceLinkLinks +} +catch { + Write-Host $_.Exception + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'SourceLink' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/symbols-validation.ps1 b/eng/common/post-build/symbols-validation.ps1 new file mode 100644 index 000000000..cd2181baf --- /dev/null +++ b/eng/common/post-build/symbols-validation.ps1 @@ -0,0 +1,339 @@ +param( + [Parameter(Mandatory = $true)][string] $InputPath, # Full path to directory where NuGet packages to be checked are stored + [Parameter(Mandatory = $true)][string] $ExtractPath, # Full path to directory where the packages will be extracted during validation + [Parameter(Mandatory = $true)][string] $DotnetSymbolVersion, # Version of dotnet symbol to use + [Parameter(Mandatory = $false)][switch] $CheckForWindowsPdbs, # If we should check for the existence of windows pdbs in addition to portable PDBs + [Parameter(Mandatory = $false)][switch] $ContinueOnError, # If we should keep checking symbols after an error + [Parameter(Mandatory = $false)][switch] $Clean, # Clean extracted symbols directory after checking symbols + [Parameter(Mandatory = $false)][string] $SymbolExclusionFile # Exclude the symbols in the file from publishing to symbol server +) + +. $PSScriptRoot\..\tools.ps1 +# Maximum number of jobs to run in parallel +$MaxParallelJobs = 16 + +# Max number of retries +$MaxRetry = 5 + +# Wait time between check for system load +$SecondsBetweenLoadChecks = 10 + +# Set error codes +Set-Variable -Name "ERROR_BADEXTRACT" -Option Constant -Value -1 +Set-Variable -Name "ERROR_FILEDOESNOTEXIST" -Option Constant -Value -2 + +$WindowsPdbVerificationParam = "" +if ($CheckForWindowsPdbs) { + $WindowsPdbVerificationParam = "--windows-pdbs" +} + +$ExclusionSet = New-Object System.Collections.Generic.HashSet[string]; + +if (!$InputPath -or !(Test-Path $InputPath)){ + Write-Host "No symbols to validate." + ExitWithExitCode 0 +} + +#Check if the path exists +if ($SymbolExclusionFile -and (Test-Path $SymbolExclusionFile)){ + [string[]]$Exclusions = Get-Content "$SymbolExclusionFile" + $Exclusions | foreach { if($_ -and $_.Trim()){$ExclusionSet.Add($_)} } +} +else{ + Write-Host "Symbol Exclusion file does not exists. No symbols to exclude." +} + +$CountMissingSymbols = { + param( + [string] $PackagePath, # Path to a NuGet package + [string] $WindowsPdbVerificationParam # If we should check for the existence of windows pdbs in addition to portable PDBs + ) + + Add-Type -AssemblyName System.IO.Compression.FileSystem + + Write-Host "Validating $PackagePath " + + # Ensure input file exist + if (!(Test-Path $PackagePath)) { + Write-PipelineTaskError "Input file does not exist: $PackagePath" + return [pscustomobject]@{ + result = $using:ERROR_FILEDOESNOTEXIST + packagePath = $PackagePath + } + } + + # Extensions for which we'll look for symbols + $RelevantExtensions = @('.dll', '.exe', '.so', '.dylib') + + # How many files are missing symbol information + $MissingSymbols = 0 + + $PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath) + $PackageGuid = New-Guid + $ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageGuid + $SymbolsPath = Join-Path -Path $ExtractPath -ChildPath 'Symbols' + + try { + [System.IO.Compression.ZipFile]::ExtractToDirectory($PackagePath, $ExtractPath) + } + catch { + Write-Host "Something went wrong extracting $PackagePath" + Write-Host $_ + return [pscustomobject]@{ + result = $using:ERROR_BADEXTRACT + packagePath = $PackagePath + } + } + + Get-ChildItem -Recurse $ExtractPath | + Where-Object { $RelevantExtensions -contains $_.Extension } | + ForEach-Object { + $FileName = $_.FullName + if ($FileName -Match '\\ref\\') { + Write-Host "`t Ignoring reference assembly file " $FileName + return + } + + $FirstMatchingSymbolDescriptionOrDefault = { + param( + [string] $FullPath, # Full path to the module that has to be checked + [string] $TargetServerParam, # Parameter to pass to `Symbol Tool` indicating the server to lookup for symbols + [string] $WindowsPdbVerificationParam, # Parameter to pass to potential check for windows-pdbs. + [string] $SymbolsPath + ) + + $FileName = [System.IO.Path]::GetFileName($FullPath) + $Extension = [System.IO.Path]::GetExtension($FullPath) + + # Those below are potential symbol files that the `dotnet symbol` might + # return. Which one will be returned depend on the type of file we are + # checking and which type of file was uploaded. + + # The file itself is returned + $SymbolPath = $SymbolsPath + '\' + $FileName + + # PDB file for the module + $PdbPath = $SymbolPath.Replace($Extension, '.pdb') + + # PDB file for R2R module (created by crossgen) + $NGenPdb = $SymbolPath.Replace($Extension, '.ni.pdb') + + # DBG file for a .so library + $SODbg = $SymbolPath.Replace($Extension, '.so.dbg') + + # DWARF file for a .dylib + $DylibDwarf = $SymbolPath.Replace($Extension, '.dylib.dwarf') + + $dotnetSymbolExe = "$env:USERPROFILE\.dotnet\tools" + $dotnetSymbolExe = Resolve-Path "$dotnetSymbolExe\dotnet-symbol.exe" + + $totalRetries = 0 + + while ($totalRetries -lt $using:MaxRetry) { + + # Save the output and get diagnostic output + $output = & $dotnetSymbolExe --symbols --modules $WindowsPdbVerificationParam $TargetServerParam $FullPath -o $SymbolsPath --diagnostics | Out-String + + if ((Test-Path $PdbPath) -and (Test-path $SymbolPath)) { + return 'Module and PDB for Module' + } + elseif ((Test-Path $NGenPdb) -and (Test-Path $PdbPath) -and (Test-Path $SymbolPath)) { + return 'Dll, PDB and NGen PDB' + } + elseif ((Test-Path $SODbg) -and (Test-Path $SymbolPath)) { + return 'So and DBG for SO' + } + elseif ((Test-Path $DylibDwarf) -and (Test-Path $SymbolPath)) { + return 'Dylib and Dwarf for Dylib' + } + elseif (Test-Path $SymbolPath) { + return 'Module' + } + else + { + $totalRetries++ + } + } + + return $null + } + + $FileRelativePath = $FileName.Replace("$ExtractPath\", "") + if (($($using:ExclusionSet) -ne $null) -and ($($using:ExclusionSet).Contains($FileRelativePath) -or ($($using:ExclusionSet).Contains($FileRelativePath.Replace("\", "/"))))){ + Write-Host "Skipping $FileName from symbol validation" + } + + else { + $FileGuid = New-Guid + $ExpandedSymbolsPath = Join-Path -Path $SymbolsPath -ChildPath $FileGuid + + $SymbolsOnMSDL = & $FirstMatchingSymbolDescriptionOrDefault ` + -FullPath $FileName ` + -TargetServerParam '--microsoft-symbol-server' ` + -SymbolsPath "$ExpandedSymbolsPath-msdl" ` + -WindowsPdbVerificationParam $WindowsPdbVerificationParam + $SymbolsOnSymWeb = & $FirstMatchingSymbolDescriptionOrDefault ` + -FullPath $FileName ` + -TargetServerParam '--internal-server' ` + -SymbolsPath "$ExpandedSymbolsPath-symweb" ` + -WindowsPdbVerificationParam $WindowsPdbVerificationParam + + Write-Host -NoNewLine "`t Checking file " $FileName "... " + + if ($SymbolsOnMSDL -ne $null -and $SymbolsOnSymWeb -ne $null) { + Write-Host "Symbols found on MSDL ($SymbolsOnMSDL) and SymWeb ($SymbolsOnSymWeb)" + } + else { + $MissingSymbols++ + + if ($SymbolsOnMSDL -eq $null -and $SymbolsOnSymWeb -eq $null) { + Write-Host 'No symbols found on MSDL or SymWeb!' + } + else { + if ($SymbolsOnMSDL -eq $null) { + Write-Host 'No symbols found on MSDL!' + } + else { + Write-Host 'No symbols found on SymWeb!' + } + } + } + } + } + + if ($using:Clean) { + Remove-Item $ExtractPath -Recurse -Force + } + + Pop-Location + + return [pscustomobject]@{ + result = $MissingSymbols + packagePath = $PackagePath + } +} + +function CheckJobResult( + $result, + $packagePath, + [ref]$DupedSymbols, + [ref]$TotalFailures) { + if ($result -eq $ERROR_BADEXTRACT) { + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$packagePath has duplicated symbol files" + $DupedSymbols.Value++ + } + elseif ($result -eq $ERROR_FILEDOESNOTEXIST) { + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$packagePath does not exist" + $TotalFailures.Value++ + } + elseif ($result -gt '0') { + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "Missing symbols for $result modules in the package $packagePath" + $TotalFailures.Value++ + } + else { + Write-Host "All symbols verified for package $packagePath" + } +} + +function CheckSymbolsAvailable { + if (Test-Path $ExtractPath) { + Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue + } + + $TotalPackages = 0 + $TotalFailures = 0 + $DupedSymbols = 0 + + Get-ChildItem "$InputPath\*.nupkg" | + ForEach-Object { + $FileName = $_.Name + $FullName = $_.FullName + + # These packages from Arcade-Services include some native libraries that + # our current symbol uploader can't handle. Below is a workaround until + # we get issue: https://github.com/dotnet/arcade/issues/2457 sorted. + if ($FileName -Match 'Microsoft\.DotNet\.Darc\.') { + Write-Host "Ignoring Arcade-services file: $FileName" + Write-Host + return + } + elseif ($FileName -Match 'Microsoft\.DotNet\.Maestro\.Tasks\.') { + Write-Host "Ignoring Arcade-services file: $FileName" + Write-Host + return + } + + $TotalPackages++ + + Start-Job -ScriptBlock $CountMissingSymbols -ArgumentList @($FullName,$WindowsPdbVerificationParam) | Out-Null + + $NumJobs = @(Get-Job -State 'Running').Count + + while ($NumJobs -ge $MaxParallelJobs) { + Write-Host "There are $NumJobs validation jobs running right now. Waiting $SecondsBetweenLoadChecks seconds to check again." + sleep $SecondsBetweenLoadChecks + $NumJobs = @(Get-Job -State 'Running').Count + } + + foreach ($Job in @(Get-Job -State 'Completed')) { + $jobResult = Wait-Job -Id $Job.Id | Receive-Job + CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$DupedSymbols) ([ref]$TotalFailures) + Remove-Job -Id $Job.Id + } + Write-Host + } + + foreach ($Job in @(Get-Job)) { + $jobResult = Wait-Job -Id $Job.Id | Receive-Job + CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$DupedSymbols) ([ref]$TotalFailures) + } + + if ($TotalFailures -gt 0 -or $DupedSymbols -gt 0) { + if ($TotalFailures -gt 0) { + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "Symbols missing for $TotalFailures/$TotalPackages packages" + } + + if ($DupedSymbols -gt 0) { + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$DupedSymbols/$TotalPackages packages had duplicated symbol files and could not be extracted" + } + + ExitWithExitCode 1 + } + else { + Write-Host "All symbols validated!" + } +} + +function InstallDotnetSymbol { + $dotnetSymbolPackageName = 'dotnet-symbol' + + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + $toolList = & "$dotnet" tool list --global + + if (($toolList -like "*$dotnetSymbolPackageName*") -and ($toolList -like "*$dotnetSymbolVersion*")) { + Write-Host "dotnet-symbol version $dotnetSymbolVersion is already installed." + } + else { + Write-Host "Installing dotnet-symbol version $dotnetSymbolVersion..." + Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.' + & "$dotnet" tool install $dotnetSymbolPackageName --version $dotnetSymbolVersion --verbosity "minimal" --global + } +} + +try { + . $PSScriptRoot\post-build-utils.ps1 + + InstallDotnetSymbol + + foreach ($Job in @(Get-Job)) { + Remove-Job -Id $Job.Id + } + + CheckSymbolsAvailable +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/post-build/trigger-subscriptions.ps1 b/eng/common/post-build/trigger-subscriptions.ps1 new file mode 100644 index 000000000..ac9a95778 --- /dev/null +++ b/eng/common/post-build/trigger-subscriptions.ps1 @@ -0,0 +1,64 @@ +param( + [Parameter(Mandatory=$true)][string] $SourceRepo, + [Parameter(Mandatory=$true)][int] $ChannelId, + [Parameter(Mandatory=$true)][string] $MaestroApiAccessToken, + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', + [Parameter(Mandatory=$false)][string] $MaestroApiVersion = '2019-01-16' +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + # Get all the $SourceRepo subscriptions + $normalizedSourceRepo = $SourceRepo.Replace('dnceng@', '') + $subscriptions = Get-MaestroSubscriptions -SourceRepository $normalizedSourceRepo -ChannelId $ChannelId + + if (!$subscriptions) { + Write-PipelineTelemetryError -Category 'TriggerSubscriptions' -Message "No subscriptions found for source repo '$normalizedSourceRepo' in channel '$ChannelId'" + ExitWithExitCode 0 + } + + $subscriptionsToTrigger = New-Object System.Collections.Generic.List[string] + $failedTriggeredSubscription = $false + + # Get all enabled subscriptions that need dependency flow on 'everyBuild' + foreach ($subscription in $subscriptions) { + if ($subscription.enabled -and $subscription.policy.updateFrequency -like 'everyBuild' -and $subscription.channel.id -eq $ChannelId) { + Write-Host "Should trigger this subscription: ${$subscription.id}" + [void]$subscriptionsToTrigger.Add($subscription.id) + } + } + + foreach ($subscriptionToTrigger in $subscriptionsToTrigger) { + try { + Write-Host "Triggering subscription '$subscriptionToTrigger'." + + Trigger-Subscription -SubscriptionId $subscriptionToTrigger + + Write-Host 'done.' + } + catch + { + Write-Host "There was an error while triggering subscription '$subscriptionToTrigger'" + Write-Host $_ + Write-Host $_.ScriptStackTrace + $failedTriggeredSubscription = $true + } + } + + if ($subscriptionsToTrigger.Count -eq 0) { + Write-Host "No subscription matched source repo '$normalizedSourceRepo' and channel ID '$ChannelId'." + } + elseif ($failedTriggeredSubscription) { + Write-PipelineTelemetryError -Category 'TriggerSubscriptions' -Message 'At least one subscription failed to be triggered...' + ExitWithExitCode 1 + } + else { + Write-Host 'All subscriptions were triggered successfully!' + } +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'TriggerSubscriptions' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/retain-build.ps1 b/eng/common/retain-build.ps1 new file mode 100644 index 000000000..e7ba975ad --- /dev/null +++ b/eng/common/retain-build.ps1 @@ -0,0 +1,45 @@ + +Param( +[Parameter(Mandatory=$true)][int] $buildId, +[Parameter(Mandatory=$true)][string] $azdoOrgUri, +[Parameter(Mandatory=$true)][string] $azdoProject, +[Parameter(Mandatory=$true)][string] $token +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 + +function Get-AzDOHeaders( + [string] $token) +{ + $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":${token}")) + $headers = @{"Authorization"="Basic $base64AuthInfo"} + return $headers +} + +function Update-BuildRetention( + [string] $azdoOrgUri, + [string] $azdoProject, + [int] $buildId, + [string] $token) +{ + $headers = Get-AzDOHeaders -token $token + $requestBody = "{ + `"keepForever`": `"true`" + }" + + $requestUri = "${azdoOrgUri}/${azdoProject}/_apis/build/builds/${buildId}?api-version=6.0" + write-Host "Attempting to retain build using the following URI: ${requestUri} ..." + + try { + Invoke-RestMethod -Uri $requestUri -Method Patch -Body $requestBody -Header $headers -contentType "application/json" + Write-Host "Updated retention settings for build ${buildId}." + } + catch { + Write-Error "Failed to update retention settings for build: $_.Exception.Response.StatusDescription" + exit 1 + } +} + +Update-BuildRetention -azdoOrgUri $azdoOrgUri -azdoProject $azdoProject -buildId $buildId -token $token +exit 0 diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 new file mode 100644 index 000000000..aab40de3f --- /dev/null +++ b/eng/common/sdk-task.ps1 @@ -0,0 +1,97 @@ +[CmdletBinding(PositionalBinding=$false)] +Param( + [string] $configuration = 'Debug', + [string] $task, + [string] $verbosity = 'minimal', + [string] $msbuildEngine = $null, + [switch] $restore, + [switch] $prepareMachine, + [switch] $help, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties +) + +$ci = $true +$binaryLog = $true +$warnAsError = $true + +. $PSScriptRoot\tools.ps1 + +function Print-Usage() { + Write-Host "Common settings:" + Write-Host " -task Name of Arcade task (name of a project in SdkTasks directory of the Arcade SDK package)" + Write-Host " -restore Restore dependencies" + Write-Host " -verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]" + Write-Host " -help Print help and exit" + Write-Host "" + + Write-Host "Advanced settings:" + Write-Host " -prepareMachine Prepare machine for CI run" + Write-Host " -msbuildEngine Msbuild engine to use to run build ('dotnet', 'vs', or unspecified)." + Write-Host "" + Write-Host "Command line arguments not listed above are passed thru to msbuild." +} + +function Build([string]$target) { + $logSuffix = if ($target -eq 'Execute') { '' } else { ".$target" } + $log = Join-Path $LogDir "$task$logSuffix.binlog" + $outputPath = Join-Path $ToolsetDir "$task\" + + MSBuild $taskProject ` + /bl:$log ` + /t:$target ` + /p:Configuration=$configuration ` + /p:RepoRoot=$RepoRoot ` + /p:BaseIntermediateOutputPath=$outputPath ` + /v:$verbosity ` + @properties +} + +try { + if ($help -or (($null -ne $properties) -and ($properties.Contains('/help') -or $properties.Contains('/?')))) { + Print-Usage + exit 0 + } + + if ($task -eq "") { + Write-PipelineTelemetryError -Category 'Build' -Message "Missing required parameter '-task '" + Print-Usage + ExitWithExitCode 1 + } + + if( $msbuildEngine -eq "vs") { + # Ensure desktop MSBuild is available for sdk tasks. + if( -not ($GlobalJson.tools.PSObject.Properties.Name -contains "vs" )) { + $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty + } + if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.10.0-pre.4.0" -MemberType NoteProperty + } + if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { + $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true + } + if ($xcopyMSBuildToolsFolder -eq $null) { + throw 'Unable to get xcopy downloadable version of msbuild' + } + + $global:_MSBuildExe = "$($xcopyMSBuildToolsFolder)\MSBuild\Current\Bin\MSBuild.exe" + } + + $taskProject = GetSdkTaskProject $task + if (!(Test-Path $taskProject)) { + Write-PipelineTelemetryError -Category 'Build' -Message "Unknown task: $task" + ExitWithExitCode 1 + } + + if ($restore) { + Build 'Restore' + } + + Build 'Execute' +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Category 'Build' -Message $_ + ExitWithExitCode 1 +} + +ExitWithExitCode 0 diff --git a/eng/common/sdl/NuGet.config b/eng/common/sdl/NuGet.config new file mode 100644 index 000000000..3849bdb3c --- /dev/null +++ b/eng/common/sdl/NuGet.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/eng/common/sdl/configure-sdl-tool.ps1 b/eng/common/sdl/configure-sdl-tool.ps1 new file mode 100644 index 000000000..27f5a4115 --- /dev/null +++ b/eng/common/sdl/configure-sdl-tool.ps1 @@ -0,0 +1,130 @@ +Param( + [string] $GuardianCliLocation, + [string] $WorkingDirectory, + [string] $TargetDirectory, + [string] $GdnFolder, + # The list of Guardian tools to configure. For each object in the array: + # - If the item is a [hashtable], it must contain these entries: + # - Name = The tool name as Guardian knows it. + # - Scenario = (Optional) Scenario-specific name for this configuration entry. It must be unique + # among all tool entries with the same Name. + # - Args = (Optional) Array of Guardian tool configuration args, like '@("Target > C:\temp")' + # - If the item is a [string] $v, it is treated as '@{ Name="$v" }' + [object[]] $ToolsList, + [string] $GuardianLoggerLevel='Standard', + # Optional: Additional params to add to any tool using CredScan. + [string[]] $CrScanAdditionalRunConfigParams, + # Optional: Additional params to add to any tool using PoliCheck. + [string[]] $PoliCheckAdditionalRunConfigParams, + # Optional: Additional params to add to any tool using CodeQL/Semmle. + [string[]] $CodeQLAdditionalRunConfigParams, + # Optional: Additional params to add to any tool using Binskim. + [string[]] $BinskimAdditionalRunConfigParams +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 +$disableConfigureToolsetImport = $true +$global:LASTEXITCODE = 0 + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + # Normalize tools list: all in [hashtable] form with defined values for each key. + $ToolsList = $ToolsList | + ForEach-Object { + if ($_ -is [string]) { + $_ = @{ Name = $_ } + } + + if (-not ($_['Scenario'])) { $_.Scenario = "" } + if (-not ($_['Args'])) { $_.Args = @() } + $_ + } + + Write-Host "List of tools to configure:" + $ToolsList | ForEach-Object { $_ | Out-String | Write-Host } + + # We store config files in the r directory of .gdn + $gdnConfigPath = Join-Path $GdnFolder 'r' + $ValidPath = Test-Path $GuardianCliLocation + + if ($ValidPath -eq $False) + { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Invalid Guardian CLI Location." + ExitWithExitCode 1 + } + + foreach ($tool in $ToolsList) { + # Put together the name and scenario to make a unique key. + $toolConfigName = $tool.Name + if ($tool.Scenario) { + $toolConfigName += "_" + $tool.Scenario + } + + Write-Host "=== Configuring $toolConfigName..." + + $gdnConfigFile = Join-Path $gdnConfigPath "$toolConfigName-configure.gdnconfig" + + # For some tools, add default and automatic args. + switch -Exact ($tool.Name) { + 'credscan' { + if ($targetDirectory) { + $tool.Args += "`"TargetDirectory < $TargetDirectory`"" + } + $tool.Args += "`"OutputType < pre`"" + $tool.Args += $CrScanAdditionalRunConfigParams + } + 'policheck' { + if ($targetDirectory) { + $tool.Args += "`"Target < $TargetDirectory`"" + } + $tool.Args += $PoliCheckAdditionalRunConfigParams + } + {$_ -in 'semmle', 'codeql'} { + if ($targetDirectory) { + $tool.Args += "`"SourceCodeDirectory < $TargetDirectory`"" + } + $tool.Args += $CodeQLAdditionalRunConfigParams + } + 'binskim' { + if ($targetDirectory) { + # Binskim crashes due to specific PDBs. GitHub issue: https://github.com/microsoft/binskim/issues/924. + # We are excluding all `_.pdb` files from the scan. + $tool.Args += "`"Target < $TargetDirectory\**;-:file|$TargetDirectory\**\_.pdb`"" + } + $tool.Args += $BinskimAdditionalRunConfigParams + } + } + + # Create variable pointing to the args array directly so we can use splat syntax later. + $toolArgs = $tool.Args + + # Configure the tool. If args array is provided or the current tool has some default arguments + # defined, add "--args" and splat each element on the end. Arg format is "{Arg id} < {Value}", + # one per parameter. Doc page for "guardian configure": + # https://dev.azure.com/securitytools/SecurityIntegration/_wiki/wikis/Guardian/1395/configure + Exec-BlockVerbosely { + & $GuardianCliLocation configure ` + --working-directory $WorkingDirectory ` + --tool $tool.Name ` + --output-path $gdnConfigFile ` + --logger-level $GuardianLoggerLevel ` + --noninteractive ` + --force ` + $(if ($toolArgs) { "--args" }) @toolArgs + Exit-IfNZEC "Sdl" + } + + Write-Host "Created '$toolConfigName' configuration file: $gdnConfigFile" + } +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/execute-all-sdl-tools.ps1 b/eng/common/sdl/execute-all-sdl-tools.ps1 new file mode 100644 index 000000000..4715d75e9 --- /dev/null +++ b/eng/common/sdl/execute-all-sdl-tools.ps1 @@ -0,0 +1,167 @@ +Param( + [string] $GuardianPackageName, # Required: the name of guardian CLI package (not needed if GuardianCliLocation is specified) + [string] $NugetPackageDirectory, # Required: directory where NuGet packages are installed (not needed if GuardianCliLocation is specified) + [string] $GuardianCliLocation, # Optional: Direct location of Guardian CLI executable if GuardianPackageName & NugetPackageDirectory are not specified + [string] $Repository=$env:BUILD_REPOSITORY_NAME, # Required: the name of the repository (e.g. dotnet/arcade) + [string] $BranchName=$env:BUILD_SOURCEBRANCH, # Optional: name of branch or version of gdn settings; defaults to master + [string] $SourceDirectory=$env:BUILD_SOURCESDIRECTORY, # Required: the directory where source files are located + [string] $ArtifactsDirectory = (Join-Path $env:BUILD_ARTIFACTSTAGINGDIRECTORY ('artifacts')), # Required: the directory where build artifacts are located + [string] $AzureDevOpsAccessToken, # Required: access token for dnceng; should be provided via KeyVault + + # Optional: list of SDL tools to run on source code. See 'configure-sdl-tool.ps1' for tools list + # format. + [object[]] $SourceToolsList, + # Optional: list of SDL tools to run on built artifacts. See 'configure-sdl-tool.ps1' for tools + # list format. + [object[]] $ArtifactToolsList, + # Optional: list of SDL tools to run without automatically specifying a target directory. See + # 'configure-sdl-tool.ps1' for tools list format. + [object[]] $CustomToolsList, + + [bool] $TsaPublish=$False, # Optional: true will publish results to TSA; only set to true after onboarding to TSA; TSA is the automated framework used to upload test results as bugs. + [string] $TsaBranchName=$env:BUILD_SOURCEBRANCH, # Optional: required for TSA publish; defaults to $(Build.SourceBranchName); TSA is the automated framework used to upload test results as bugs. + [string] $TsaRepositoryName=$env:BUILD_REPOSITORY_NAME, # Optional: TSA repository name; will be generated automatically if not submitted; TSA is the automated framework used to upload test results as bugs. + [string] $BuildNumber=$env:BUILD_BUILDNUMBER, # Optional: required for TSA publish; defaults to $(Build.BuildNumber) + [bool] $UpdateBaseline=$False, # Optional: if true, will update the baseline in the repository; should only be run after fixing any issues which need to be fixed + [bool] $TsaOnboard=$False, # Optional: if true, will onboard the repository to TSA; should only be run once; TSA is the automated framework used to upload test results as bugs. + [string] $TsaInstanceUrl, # Optional: only needed if TsaOnboard or TsaPublish is true; the instance-url registered with TSA; TSA is the automated framework used to upload test results as bugs. + [string] $TsaCodebaseName, # Optional: only needed if TsaOnboard or TsaPublish is true; the name of the codebase registered with TSA; TSA is the automated framework used to upload test results as bugs. + [string] $TsaProjectName, # Optional: only needed if TsaOnboard or TsaPublish is true; the name of the project registered with TSA; TSA is the automated framework used to upload test results as bugs. + [string] $TsaNotificationEmail, # Optional: only needed if TsaOnboard is true; the email(s) which will receive notifications of TSA bug filings (e.g. alias@microsoft.com); TSA is the automated framework used to upload test results as bugs. + [string] $TsaCodebaseAdmin, # Optional: only needed if TsaOnboard is true; the aliases which are admins of the TSA codebase (e.g. DOMAIN\alias); TSA is the automated framework used to upload test results as bugs. + [string] $TsaBugAreaPath, # Optional: only needed if TsaOnboard is true; the area path where TSA will file bugs in AzDO; TSA is the automated framework used to upload test results as bugs. + [string] $TsaIterationPath, # Optional: only needed if TsaOnboard is true; the iteration path where TSA will file bugs in AzDO; TSA is the automated framework used to upload test results as bugs. + [string] $GuardianLoggerLevel='Standard', # Optional: the logger level for the Guardian CLI; options are Trace, Verbose, Standard, Warning, and Error + [string[]] $CrScanAdditionalRunConfigParams, # Optional: Additional Params to custom build a CredScan run config in the format @("xyz:abc","sdf:1") + [string[]] $PoliCheckAdditionalRunConfigParams, # Optional: Additional Params to custom build a Policheck run config in the format @("xyz:abc","sdf:1") + [string[]] $CodeQLAdditionalRunConfigParams, # Optional: Additional Params to custom build a Semmle/CodeQL run config in the format @("xyz < abc","sdf < 1") + [string[]] $BinskimAdditionalRunConfigParams, # Optional: Additional Params to custom build a Binskim run config in the format @("xyz < abc","sdf < 1") + [bool] $BreakOnFailure=$False # Optional: Fail the build if there were errors during the run +) + +try { + $ErrorActionPreference = 'Stop' + Set-StrictMode -Version 2.0 + $disableConfigureToolsetImport = $true + $global:LASTEXITCODE = 0 + + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + #Replace repo names to the format of org/repo + if (!($Repository.contains('/'))) { + $RepoName = $Repository -replace '(.*?)-(.*)', '$1/$2'; + } + else{ + $RepoName = $Repository; + } + + if ($GuardianPackageName) { + $guardianCliLocation = Join-Path $NugetPackageDirectory (Join-Path $GuardianPackageName (Join-Path 'tools' 'guardian.cmd')) + } else { + $guardianCliLocation = $GuardianCliLocation + } + + $workingDirectory = (Split-Path $SourceDirectory -Parent) + $ValidPath = Test-Path $guardianCliLocation + + if ($ValidPath -eq $False) + { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message 'Invalid Guardian CLI Location.' + ExitWithExitCode 1 + } + + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'init-sdl.ps1') -GuardianCliLocation $guardianCliLocation -Repository $RepoName -BranchName $BranchName -WorkingDirectory $workingDirectory -AzureDevOpsAccessToken $AzureDevOpsAccessToken -GuardianLoggerLevel $GuardianLoggerLevel + } + $gdnFolder = Join-Path $workingDirectory '.gdn' + + if ($TsaOnboard) { + if ($TsaCodebaseName -and $TsaNotificationEmail -and $TsaCodebaseAdmin -and $TsaBugAreaPath) { + Exec-BlockVerbosely { + & $guardianCliLocation tsa-onboard --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } + if ($LASTEXITCODE -ne 0) { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian tsa-onboard failed with exit code $LASTEXITCODE." + ExitWithExitCode $LASTEXITCODE + } + } else { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message 'Could not onboard to TSA -- not all required values ($TsaCodebaseName, $TsaNotificationEmail, $TsaCodebaseAdmin, $TsaBugAreaPath) were specified.' + ExitWithExitCode 1 + } + } + + # Configure a list of tools with a default target directory. Populates the ".gdn/r" directory. + function Configure-ToolsList([object[]] $tools, [string] $targetDirectory) { + if ($tools -and $tools.Count -gt 0) { + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'configure-sdl-tool.ps1') ` + -GuardianCliLocation $guardianCliLocation ` + -WorkingDirectory $workingDirectory ` + -TargetDirectory $targetDirectory ` + -GdnFolder $gdnFolder ` + -ToolsList $tools ` + -AzureDevOpsAccessToken $AzureDevOpsAccessToken ` + -GuardianLoggerLevel $GuardianLoggerLevel ` + -CrScanAdditionalRunConfigParams $CrScanAdditionalRunConfigParams ` + -PoliCheckAdditionalRunConfigParams $PoliCheckAdditionalRunConfigParams ` + -CodeQLAdditionalRunConfigParams $CodeQLAdditionalRunConfigParams ` + -BinskimAdditionalRunConfigParams $BinskimAdditionalRunConfigParams + if ($BreakOnFailure) { + Exit-IfNZEC "Sdl" + } + } + } + } + + # Configure Artifact and Source tools with default Target directories. + Configure-ToolsList $ArtifactToolsList $ArtifactsDirectory + Configure-ToolsList $SourceToolsList $SourceDirectory + # Configure custom tools with no default Target directory. + Configure-ToolsList $CustomToolsList $null + + # At this point, all tools are configured in the ".gdn" directory. Run them all in a single call. + # (If we used "run" multiple times, each run would overwrite data from earlier runs.) + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'run-sdl.ps1') ` + -GuardianCliLocation $guardianCliLocation ` + -WorkingDirectory $SourceDirectory ` + -UpdateBaseline $UpdateBaseline ` + -GdnFolder $gdnFolder + } + + if ($TsaPublish) { + if ($TsaBranchName -and $BuildNumber) { + if (-not $TsaRepositoryName) { + $TsaRepositoryName = "$($Repository)-$($BranchName)" + } + Exec-BlockVerbosely { + & $guardianCliLocation tsa-publish --all-tools --repository-name "$TsaRepositoryName" --branch-name "$TsaBranchName" --build-number "$BuildNumber" --onboard $True --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } + if ($LASTEXITCODE -ne 0) { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian tsa-publish failed with exit code $LASTEXITCODE." + ExitWithExitCode $LASTEXITCODE + } + } else { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message 'Could not publish to TSA -- not all required values ($TsaBranchName, $BuildNumber) were specified.' + ExitWithExitCode 1 + } + } + + if ($BreakOnFailure) { + Write-Host "Failing the build in case of breaking results..." + Exec-BlockVerbosely { + & $guardianCliLocation break --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } + } else { + Write-Host "Letting the build pass even if there were breaking results..." + } +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + exit 1 +} diff --git a/eng/common/sdl/extract-artifact-archives.ps1 b/eng/common/sdl/extract-artifact-archives.ps1 new file mode 100644 index 000000000..68da4fbf2 --- /dev/null +++ b/eng/common/sdl/extract-artifact-archives.ps1 @@ -0,0 +1,63 @@ +# This script looks for each archive file in a directory and extracts it into the target directory. +# For example, the file "$InputPath/bin.tar.gz" extracts to "$ExtractPath/bin.tar.gz.extracted/**". +# Uses the "tar" utility added to Windows 10 / Windows 2019 that supports tar.gz and zip. +param( + # Full path to directory where archives are stored. + [Parameter(Mandatory=$true)][string] $InputPath, + # Full path to directory to extract archives into. May be the same as $InputPath. + [Parameter(Mandatory=$true)][string] $ExtractPath +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 + +$disableConfigureToolsetImport = $true + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + Measure-Command { + $jobs = @() + + # Find archive files for non-Windows and Windows builds. + $archiveFiles = @( + Get-ChildItem (Join-Path $InputPath "*.tar.gz") + Get-ChildItem (Join-Path $InputPath "*.zip") + ) + + foreach ($targzFile in $archiveFiles) { + $jobs += Start-Job -ScriptBlock { + $file = $using:targzFile + $fileName = [System.IO.Path]::GetFileName($file) + $extractDir = Join-Path $using:ExtractPath "$fileName.extracted" + + New-Item $extractDir -ItemType Directory -Force | Out-Null + + Write-Host "Extracting '$file' to '$extractDir'..." + + # Pipe errors to stdout to prevent PowerShell detecting them and quitting the job early. + # This type of quit skips the catch, so we wouldn't be able to tell which file triggered the + # error. Save output so it can be stored in the exception string along with context. + $output = tar -xf $file -C $extractDir 2>&1 + # Handle NZEC manually rather than using Exit-IfNZEC: we are in a background job, so we + # don't have access to the outer scope. + if ($LASTEXITCODE -ne 0) { + throw "Error extracting '$file': non-zero exit code ($LASTEXITCODE). Output: '$output'" + } + + Write-Host "Extracted to $extractDir" + } + } + + Receive-Job $jobs -Wait + } +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/extract-artifact-packages.ps1 b/eng/common/sdl/extract-artifact-packages.ps1 new file mode 100644 index 000000000..f031ed5b2 --- /dev/null +++ b/eng/common/sdl/extract-artifact-packages.ps1 @@ -0,0 +1,82 @@ +param( + [Parameter(Mandatory=$true)][string] $InputPath, # Full path to directory where artifact packages are stored + [Parameter(Mandatory=$true)][string] $ExtractPath # Full path to directory where the packages will be extracted +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 + +$disableConfigureToolsetImport = $true + +function ExtractArtifacts { + if (!(Test-Path $InputPath)) { + Write-Host "Input Path does not exist: $InputPath" + ExitWithExitCode 0 + } + $Jobs = @() + Get-ChildItem "$InputPath\*.nupkg" | + ForEach-Object { + $Jobs += Start-Job -ScriptBlock $ExtractPackage -ArgumentList $_.FullName + } + + foreach ($Job in $Jobs) { + Wait-Job -Id $Job.Id | Receive-Job + } +} + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + $ExtractPackage = { + param( + [string] $PackagePath # Full path to a NuGet package + ) + + if (!(Test-Path $PackagePath)) { + Write-PipelineTelemetryError -Category 'Build' -Message "Input file does not exist: $PackagePath" + ExitWithExitCode 1 + } + + $RelevantExtensions = @('.dll', '.exe', '.pdb') + Write-Host -NoNewLine 'Extracting ' ([System.IO.Path]::GetFileName($PackagePath)) '...' + + $PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath) + $ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageId + + Add-Type -AssemblyName System.IO.Compression.FileSystem + + [System.IO.Directory]::CreateDirectory($ExtractPath); + + try { + $zip = [System.IO.Compression.ZipFile]::OpenRead($PackagePath) + + $zip.Entries | + Where-Object {$RelevantExtensions -contains [System.IO.Path]::GetExtension($_.Name)} | + ForEach-Object { + $TargetPath = Join-Path -Path $ExtractPath -ChildPath (Split-Path -Path $_.FullName) + [System.IO.Directory]::CreateDirectory($TargetPath); + + $TargetFile = Join-Path -Path $ExtractPath -ChildPath $_.FullName + [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile) + } + } + catch { + Write-Host $_ + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 + } + finally { + $zip.Dispose() + } + } + Measure-Command { ExtractArtifacts } +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/init-sdl.ps1 b/eng/common/sdl/init-sdl.ps1 new file mode 100644 index 000000000..3ac1d92b3 --- /dev/null +++ b/eng/common/sdl/init-sdl.ps1 @@ -0,0 +1,55 @@ +Param( + [string] $GuardianCliLocation, + [string] $Repository, + [string] $BranchName='master', + [string] $WorkingDirectory, + [string] $AzureDevOpsAccessToken, + [string] $GuardianLoggerLevel='Standard' +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 +$disableConfigureToolsetImport = $true +$global:LASTEXITCODE = 0 + +# `tools.ps1` checks $ci to perform some actions. Since the SDL +# scripts don't necessarily execute in the same agent that run the +# build.ps1/sh script this variable isn't automatically set. +$ci = $true +. $PSScriptRoot\..\tools.ps1 + +# Don't display the console progress UI - it's a huge perf hit +$ProgressPreference = 'SilentlyContinue' + +# Construct basic auth from AzDO access token; construct URI to the repository's gdn folder stored in that repository; construct location of zip file +$encodedPat = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$AzureDevOpsAccessToken")) +$escapedRepository = [Uri]::EscapeDataString("/$Repository/$BranchName/.gdn") +$uri = "https://dev.azure.com/dnceng/internal/_apis/git/repositories/sdl-tool-cfg/Items?path=$escapedRepository&versionDescriptor[versionOptions]=0&`$format=zip&api-version=5.0" +$zipFile = "$WorkingDirectory/gdn.zip" + +Add-Type -AssemblyName System.IO.Compression.FileSystem +$gdnFolder = (Join-Path $WorkingDirectory '.gdn') + +try { + # if the folder does not exist, we'll do a guardian init and push it to the remote repository + Write-Host 'Initializing Guardian...' + Write-Host "$GuardianCliLocation init --working-directory $WorkingDirectory --logger-level $GuardianLoggerLevel" + & $GuardianCliLocation init --working-directory $WorkingDirectory --logger-level $GuardianLoggerLevel + if ($LASTEXITCODE -ne 0) { + Write-PipelineTelemetryError -Force -Category 'Build' -Message "Guardian init failed with exit code $LASTEXITCODE." + ExitWithExitCode $LASTEXITCODE + } + # We create the mainbaseline so it can be edited later + Write-Host "$GuardianCliLocation baseline --working-directory $WorkingDirectory --name mainbaseline" + & $GuardianCliLocation baseline --working-directory $WorkingDirectory --name mainbaseline + if ($LASTEXITCODE -ne 0) { + Write-PipelineTelemetryError -Force -Category 'Build' -Message "Guardian baseline failed with exit code $LASTEXITCODE." + ExitWithExitCode $LASTEXITCODE + } + ExitWithExitCode 0 +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/packages.config b/eng/common/sdl/packages.config new file mode 100644 index 000000000..4585cfd6b --- /dev/null +++ b/eng/common/sdl/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/eng/common/sdl/run-sdl.ps1 b/eng/common/sdl/run-sdl.ps1 new file mode 100644 index 000000000..2eac8c78f --- /dev/null +++ b/eng/common/sdl/run-sdl.ps1 @@ -0,0 +1,49 @@ +Param( + [string] $GuardianCliLocation, + [string] $WorkingDirectory, + [string] $GdnFolder, + [string] $UpdateBaseline, + [string] $GuardianLoggerLevel='Standard' +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 +$disableConfigureToolsetImport = $true +$global:LASTEXITCODE = 0 + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + # We store config files in the r directory of .gdn + $gdnConfigPath = Join-Path $GdnFolder 'r' + $ValidPath = Test-Path $GuardianCliLocation + + if ($ValidPath -eq $False) + { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Invalid Guardian CLI Location." + ExitWithExitCode 1 + } + + $gdnConfigFiles = Get-ChildItem $gdnConfigPath -Recurse -Include '*.gdnconfig' + Write-Host "Discovered Guardian config files:" + $gdnConfigFiles | Out-String | Write-Host + + Exec-BlockVerbosely { + & $GuardianCliLocation run ` + --working-directory $WorkingDirectory ` + --baseline mainbaseline ` + --update-baseline $UpdateBaseline ` + --logger-level $GuardianLoggerLevel ` + --config @gdnConfigFiles + Exit-IfNZEC "Sdl" + } +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/sdl.ps1 b/eng/common/sdl/sdl.ps1 new file mode 100644 index 000000000..648c5068d --- /dev/null +++ b/eng/common/sdl/sdl.ps1 @@ -0,0 +1,38 @@ + +function Install-Gdn { + param( + [Parameter(Mandatory=$true)] + [string]$Path, + + # If omitted, install the latest version of Guardian, otherwise install that specific version. + [string]$Version + ) + + $ErrorActionPreference = 'Stop' + Set-StrictMode -Version 2.0 + $disableConfigureToolsetImport = $true + $global:LASTEXITCODE = 0 + + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + $argumentList = @("install", "Microsoft.Guardian.Cli", "-Source https://securitytools.pkgs.visualstudio.com/_packaging/Guardian/nuget/v3/index.json", "-OutputDirectory $Path", "-NonInteractive", "-NoCache") + + if ($Version) { + $argumentList += "-Version $Version" + } + + Start-Process nuget -Verbose -ArgumentList $argumentList -NoNewWindow -Wait + + $gdnCliPath = Get-ChildItem -Filter guardian.cmd -Recurse -Path $Path + + if (!$gdnCliPath) + { + Write-PipelineTelemetryError -Category 'Sdl' -Message 'Failure installing Guardian' + } + + return $gdnCliPath.FullName +} \ No newline at end of file diff --git a/eng/common/sdl/trim-assets-version.ps1 b/eng/common/sdl/trim-assets-version.ps1 new file mode 100644 index 000000000..0daa2a9e9 --- /dev/null +++ b/eng/common/sdl/trim-assets-version.ps1 @@ -0,0 +1,75 @@ +<# +.SYNOPSIS +Install and run the 'Microsoft.DotNet.VersionTools.Cli' tool with the 'trim-artifacts-version' command to trim the version from the NuGet assets file name. + +.PARAMETER InputPath +Full path to directory where artifact packages are stored + +.PARAMETER Recursive +Search for NuGet packages recursively + +#> + +Param( + [string] $InputPath, + [bool] $Recursive = $true +) + +$CliToolName = "Microsoft.DotNet.VersionTools.Cli" + +function Install-VersionTools-Cli { + param( + [Parameter(Mandatory=$true)][string]$Version + ) + + Write-Host "Installing the package '$CliToolName' with a version of '$version' ..." + $feed = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" + + $argumentList = @("tool", "install", "--local", "$CliToolName", "--add-source $feed", "--no-cache", "--version $Version", "--create-manifest-if-needed") + Start-Process "$dotnet" -Verbose -ArgumentList $argumentList -NoNewWindow -Wait +} + +# ------------------------------------------------------------------- + +if (!(Test-Path $InputPath)) { + Write-Host "Input Path '$InputPath' does not exist" + ExitWithExitCode 1 +} + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 + +$disableConfigureToolsetImport = $true +$global:LASTEXITCODE = 0 + +# `tools.ps1` checks $ci to perform some actions. Since the SDL +# scripts don't necessarily execute in the same agent that run the +# build.ps1/sh script this variable isn't automatically set. +$ci = $true +. $PSScriptRoot\..\tools.ps1 + +try { + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + + $toolsetVersion = Read-ArcadeSdkVersion + Install-VersionTools-Cli -Version $toolsetVersion + + $cliToolFound = (& "$dotnet" tool list --local | Where-Object {$_.Split(' ')[0] -eq $CliToolName}) + if ($null -eq $cliToolFound) { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "The '$CliToolName' tool is not installed." + ExitWithExitCode 1 + } + + Exec-BlockVerbosely { + & "$dotnet" $CliToolName trim-assets-version ` + --assets-path $InputPath ` + --recursive $Recursive + Exit-IfNZEC "Sdl" + } +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md new file mode 100644 index 000000000..c114bc28d --- /dev/null +++ b/eng/common/template-guidance.md @@ -0,0 +1,137 @@ +# Overview + +Arcade provides templates for public (`/templates`) and 1ES pipeline templates (`/templates-official`) scenarios. Pipelines which are required to be managed by 1ES pipeline templates should reference `/templates-offical`, all other pipelines may reference `/templates`. + +## How to use + +Basic guidance is: + +- 1ES Pipeline Template or 1ES Microbuild template runs should reference `eng/common/templates-official`. Any internal production-graded pipeline should use these templates. + +- All other runs should reference `eng/common/templates`. + +See [azure-pipelines.yml](../../azure-pipelines.yml) (templates-official example) or [azure-pipelines-pr.yml](../../azure-pipelines-pr.yml) (templates example) for examples. + +#### The `templateIs1ESManaged` parameter + +The `templateIs1ESManaged` is available on most templates and affects which of the variants is used for nested templates. See [Development Notes](#development-notes) below for more information on the `templateIs1ESManaged1 parameter. + +- For templates under `job/`, `jobs/`, `steps`, or `post-build/`, this parameter must be explicitly set. + +## Multiple outputs + +1ES pipeline templates impose a policy where every publish artifact execution results in additional security scans being injected into your pipeline. When using `templates-official/jobs/jobs.yml`, Arcade reduces the number of additional security injections by gathering all publishing outputs into the [Build.ArtifactStagingDirectory](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services), and utilizing the [outputParentDirectory](https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/1es-pipeline-templates/features/outputs#multiple-outputs) feature of 1ES pipeline templates. When implementing your pipeline, if you ensure publish artifacts are located in the `$(Build.ArtifactStagingDirectory)`, and utilize the 1ES provided template context, then you can reduce the number of security scans for your pipeline. + +Example: +``` yaml +# azure-pipelines.yml +extends: + template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate + parameters: + stages: + - stage: build + jobs: + - template: /eng/common/templates-official/jobs/jobs.yml@self + parameters: + # 1ES makes use of outputs to reduce security task injection overhead + templateContext: + outputs: + - output: pipelineArtifact + displayName: 'Publish logs from source' + continueOnError: true + condition: always() + targetPath: $(Build.ArtifactStagingDirectory)/artifacts/log + artifactName: Logs + jobs: + - job: Windows + steps: + - script: echo "friendly neighborhood" > artifacts/marvel/spiderman.txt + # copy build outputs to artifact staging directory for publishing + - task: CopyFiles@2 + displayName: Gather build output + inputs: + SourceFolder: '$(Build.SourcesDirectory)/artifacts/marvel' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/marvel' +``` + +Note: Multiple outputs are ONLY applicable to 1ES PT publishing (only usable when referencing `templates-official`). + +# Development notes + +**Folder / file structure** + +``` text +eng\common\ + [templates || templates-official]\ + job\ + job.yml (shim + artifact publishing logic) + onelocbuild.yml (shim) + publish-build-assets.yml (shim) + source-build.yml (shim) + source-index-stage1.yml (shim) + jobs\ + codeql-build.yml (shim) + jobs.yml (shim) + source-build.yml (shim) + post-build\ + post-build.yml (shim) + trigger-subscription.yml (shim) + common-variabls.yml (shim) + setup-maestro-vars.yml (shim) + steps\ + publish-build-artifacts.yml (logic) + publish-pipeline-artifacts.yml (logic) + add-build-channel.yml (shim) + component-governance.yml (shim) + generate-sbom.yml (shim) + publish-logs.yml (shim) + retain-build.yml (shim) + send-to-helix.yml (shim) + source-build.yml (shim) + variables\ + pool-providers.yml (logic + redirect) # templates/variables/pool-providers.yml will redirect to templates-official/variables/pool-providers.yml if you are running in the internal project + sdl-variables.yml (logic) + core-templates\ + job\ + job.yml (logic) + onelocbuild.yml (logic) + publish-build-assets.yml (logic) + source-build.yml (logic) + source-index-stage1.yml (logic) + jobs\ + codeql-build.yml (logic) + jobs.yml (logic) + source-build.yml (logic) + post-build\ + common-variabls.yml (logic) + post-build.yml (logic) + setup-maestro-vars.yml (logic) + trigger-subscription.yml (logic) + steps\ + add-build-to-channel.yml (logic) + component-governance.yml (logic) + generate-sbom.yml (logic) + publish-build-artifacts.yml (redirect) + publish-logs.yml (logic) + publish-pipeline-artifacts.yml (redirect) + retain-build.yml (logic) + send-to-helix.yml (logic) + source-build.yml (logic) + variables\ + pool-providers.yml (redirect) +``` + +In the table above, a file is designated as "shim", "logic", or "redirect". + +- shim - represents a yaml file which is an intermediate step between pipeline logic and .Net Core Engineering's templates (`core-templates`) and defines the `is1ESPipeline` parameter value. + +- logic - represents actual base template logic. + +- redirect- represents a file in `core-templates` which redirects to the "logic" file in either `templates` or `templates-official`. + +Logic for Arcade's templates live **primarily** in the `core-templates` folder. The exceptions to the location of the logic files are around artifact publishing, which is handled differently between 1es pipeline templates and standard templates. `templates` and `templates-official` provide shim entry points which redirect to `core-templates` while also defining the `is1ESPipeline` parameter. If a shim is referenced in `templates`, then `is1ESPipeline` is set to `false`. If a shim is referenced in `templates-official`, then `is1ESPipeline` is set to `true`. + +Within `templates` and `templates-official`, the templates at the "stages", and "jobs" / "job" level have been replaced with shims. Templates at the "steps" and "variables" level are typically too granular to be replaced with shims and instead persist logic which is directly applicable to either scenario. + +Within `core-templates`, there are a handful of places where logic is dependent on which shim entry point was used. In those places, we redirect back to the respective logic file in `templates` or `templates-official`. diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml new file mode 100644 index 000000000..4724e9aaa --- /dev/null +++ b/eng/common/templates-official/job/job.yml @@ -0,0 +1,62 @@ +jobs: +- template: /eng/common/core-templates/job/job.yml + parameters: + is1ESPipeline: true + + # publish artifacts + # for 1ES managed templates, use the templateContext.output to handle multiple outputs. + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - output: buildArtifacts + displayName: Publish pipeline artifacts + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} + condition: always() + continueOnError: true + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - output: pipelineArtifact + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log' + artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)_Attempt$(System.JobAttempt)') }} + displayName: 'Publish logs' + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enablePublishBuildArtifacts, true) }}: + - output: buildArtifacts + displayName: Publish Logs + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + publishLocation: Container + ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - output: pipelineArtifact + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/eng/common/BuildConfiguration' + artifactName: 'BuildConfiguration' + displayName: 'Publish build retry configuration' + continueOnError: true + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: + - output: pipelineArtifact + displayName: Publish SBOM manifest + continueOnError: true + targetPath: $(Build.ArtifactStagingDirectory)/sbom + artifactName: $(ARTIFACT_NAME) + + # add any outputs provided via root yaml + - ${{ if ne(parameters.templateContext.outputs, '') }}: + - ${{ each output in parameters.templateContext.outputs }}: + - ${{ output }} + + # add any remaining templateContext properties + ${{ each context in parameters.templateContext }}: + ${{ if and(ne(context.key, 'outputParentDirectory'), ne(context.key, 'outputs')) }}: + ${{ context.key }}: ${{ context.value }} + + ${{ each parameter in parameters }}: + ${{ if and(ne(parameter.key, 'templateContext'), ne(parameter.key, 'is1ESPipeline')) }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/onelocbuild.yml b/eng/common/templates-official/job/onelocbuild.yml new file mode 100644 index 000000000..0f0c514b9 --- /dev/null +++ b/eng/common/templates-official/job/onelocbuild.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/onelocbuild.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/publish-build-assets.yml b/eng/common/templates-official/job/publish-build-assets.yml new file mode 100644 index 000000000..d667a70e8 --- /dev/null +++ b/eng/common/templates-official/job/publish-build-assets.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/publish-build-assets.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/source-build.yml b/eng/common/templates-official/job/source-build.yml new file mode 100644 index 000000000..1a480034b --- /dev/null +++ b/eng/common/templates-official/job/source-build.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/source-index-stage1.yml b/eng/common/templates-official/job/source-index-stage1.yml new file mode 100644 index 000000000..6d5ead316 --- /dev/null +++ b/eng/common/templates-official/job/source-index-stage1.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/source-index-stage1.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/codeql-build.yml b/eng/common/templates-official/jobs/codeql-build.yml new file mode 100644 index 000000000..a726322ec --- /dev/null +++ b/eng/common/templates-official/jobs/codeql-build.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/jobs/codeql-build.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/jobs.yml b/eng/common/templates-official/jobs/jobs.yml new file mode 100644 index 000000000..007deddae --- /dev/null +++ b/eng/common/templates-official/jobs/jobs.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/source-build.yml b/eng/common/templates-official/jobs/source-build.yml new file mode 100644 index 000000000..483e7b611 --- /dev/null +++ b/eng/common/templates-official/jobs/source-build.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/post-build/common-variables.yml b/eng/common/templates-official/post-build/common-variables.yml new file mode 100644 index 000000000..c32fc4923 --- /dev/null +++ b/eng/common/templates-official/post-build/common-variables.yml @@ -0,0 +1,8 @@ +variables: +- template: /eng/common/core-templates/post-build/common-variables.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/post-build/post-build.yml b/eng/common/templates-official/post-build/post-build.yml new file mode 100644 index 000000000..2364c0fd4 --- /dev/null +++ b/eng/common/templates-official/post-build/post-build.yml @@ -0,0 +1,8 @@ +stages: +- template: /eng/common/core-templates/post-build/post-build.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/post-build/setup-maestro-vars.yml b/eng/common/templates-official/post-build/setup-maestro-vars.yml new file mode 100644 index 000000000..024397d87 --- /dev/null +++ b/eng/common/templates-official/post-build/setup-maestro-vars.yml @@ -0,0 +1,8 @@ +steps: +- template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/post-build/trigger-subscription.yml b/eng/common/templates-official/post-build/trigger-subscription.yml new file mode 100644 index 000000000..da669030d --- /dev/null +++ b/eng/common/templates-official/post-build/trigger-subscription.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Triggering subscriptions + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/trigger-subscriptions.ps1 + arguments: -SourceRepo $(Build.Repository.Uri) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/templates-official/steps/add-build-to-channel.yml b/eng/common/templates-official/steps/add-build-to-channel.yml new file mode 100644 index 000000000..543dea8c6 --- /dev/null +++ b/eng/common/templates-official/steps/add-build-to-channel.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/add-build-to-channel.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/component-governance.yml b/eng/common/templates-official/steps/component-governance.yml new file mode 100644 index 000000000..30bb3985c --- /dev/null +++ b/eng/common/templates-official/steps/component-governance.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/generate-sbom.yml b/eng/common/templates-official/steps/generate-sbom.yml new file mode 100644 index 000000000..9a89a4706 --- /dev/null +++ b/eng/common/templates-official/steps/generate-sbom.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/publish-build-artifacts.yml b/eng/common/templates-official/steps/publish-build-artifacts.yml new file mode 100644 index 000000000..100a3fc98 --- /dev/null +++ b/eng/common/templates-official/steps/publish-build-artifacts.yml @@ -0,0 +1,41 @@ +parameters: +- name: displayName + type: string + default: 'Publish to Build Artifact' + +- name: condition + type: string + default: succeeded() + +- name: artifactName + type: string + +- name: pathToPublish + type: string + +- name: continueOnError + type: boolean + default: false + +- name: publishLocation + type: string + default: 'Container' + +- name: is1ESPipeline + type: boolean + default: true + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates-official cannot be referenced from a non-1ES managed template': error +- task: 1ES.PublishBuildArtifacts@1 + displayName: ${{ parameters.displayName }} + condition: ${{ parameters.condition }} + ${{ if parameters.continueOnError }}: + continueOnError: ${{ parameters.continueOnError }} + inputs: + PublishLocation: ${{ parameters.publishLocation }} + PathtoPublish: ${{ parameters.pathToPublish }} + ${{ if parameters.artifactName }}: + ArtifactName: ${{ parameters.artifactName }} + diff --git a/eng/common/templates-official/steps/publish-logs.yml b/eng/common/templates-official/steps/publish-logs.yml new file mode 100644 index 000000000..579fd531e --- /dev/null +++ b/eng/common/templates-official/steps/publish-logs.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml new file mode 100644 index 000000000..d71eb0c74 --- /dev/null +++ b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,26 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: true + +- name: args + type: object + default: {} + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates-official cannot be referenced from a non-1ES managed template': error +- task: 1ES.PublishPipelineArtifact@1 + displayName: ${{ coalesce(parameters.args.displayName, 'Publish to Build Artifact') }} + ${{ if parameters.args.condition }}: + condition: ${{ parameters.args.condition }} + ${{ else }}: + condition: succeeded() + ${{ if parameters.args.continueOnError }}: + continueOnError: ${{ parameters.args.continueOnError }} + inputs: + targetPath: ${{ parameters.args.targetPath }} + ${{ if parameters.args.artifactName }}: + artifactName: ${{ parameters.args.artifactName }} + ${{ if parameters.args.properties }}: + properties: ${{ parameters.args.properties }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/retain-build.yml b/eng/common/templates-official/steps/retain-build.yml new file mode 100644 index 000000000..559455150 --- /dev/null +++ b/eng/common/templates-official/steps/retain-build.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/retain-build.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/send-to-helix.yml b/eng/common/templates-official/steps/send-to-helix.yml new file mode 100644 index 000000000..6500f21bf --- /dev/null +++ b/eng/common/templates-official/steps/send-to-helix.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/send-to-helix.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/source-build.yml b/eng/common/templates-official/steps/source-build.yml new file mode 100644 index 000000000..8f92c49e7 --- /dev/null +++ b/eng/common/templates-official/steps/source-build.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/variables/pool-providers.yml b/eng/common/templates-official/variables/pool-providers.yml new file mode 100644 index 000000000..1f308b24e --- /dev/null +++ b/eng/common/templates-official/variables/pool-providers.yml @@ -0,0 +1,45 @@ +# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool, +# otherwise it should go into the "normal" pools. This separates out the queueing and billing of released branches. + +# Motivation: +# Once a given branch of a repository's output has been officially "shipped" once, it is then considered to be COGS +# (Cost of goods sold) and should be moved to a servicing pool provider. This allows both separation of queueing +# (allowing release builds and main PR builds to not intefere with each other) and billing (required for COGS. +# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services +# team needs to move resources around and create new and potentially differently-named pools. Using this template +# file from an Arcade-ified repo helps guard against both having to update one's release/* branches and renaming. + +# How to use: +# This yaml assumes your shipped product branches use the naming convention "release/..." (which many do). +# If we find alternate naming conventions in broad usage it can be added to the condition below. +# +# First, import the template in an arcade-ified repo to pick up the variables, e.g.: +# +# variables: +# - template: /eng/common/templates-official/variables/pool-providers.yml +# +# ... then anywhere specifying the pool provider use the runtime variables, +# $(DncEngInternalBuildPool) +# +# pool: +# name: $(DncEngInternalBuildPool) +# image: 1es-windows-2022 + +variables: + # Coalesce the target and source branches so we know when a PR targets a release branch + # If these variables are somehow missing, fall back to main (tends to have more capacity) + + # Any new -Svc alternative pools should have variables added here to allow for splitting work + + - name: DncEngInternalBuildPool + value: $[ + replace( + replace( + eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), + True, + 'NetCore1ESPool-Svc-Internal' + ), + False, + 'NetCore1ESPool-Internal' + ) + ] \ No newline at end of file diff --git a/eng/common/templates-official/variables/sdl-variables.yml b/eng/common/templates-official/variables/sdl-variables.yml new file mode 100644 index 000000000..dbdd66d4a --- /dev/null +++ b/eng/common/templates-official/variables/sdl-variables.yml @@ -0,0 +1,7 @@ +variables: +# The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in +# sync with the packages.config file. +- name: DefaultGuardianVersion + value: 0.109.0 +- name: GuardianPackagesConfigFile + value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config \ No newline at end of file diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml new file mode 100644 index 000000000..1cf9a6d48 --- /dev/null +++ b/eng/common/templates/job/job.yml @@ -0,0 +1,61 @@ +parameters: + enablePublishBuildArtifacts: false + +jobs: +- template: /eng/common/core-templates/job/job.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ if and(ne(parameter.key, 'steps'), ne(parameter.key, 'is1ESPipeline')) }}: + ${{ parameter.key }}: ${{ parameter.value }} + + steps: + - ${{ each step in parameters.steps }}: + - ${{ step }} + + artifactPublishSteps: + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: false + args: + displayName: Publish pipeline artifacts + pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + publishLocation: Container + artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} + continueOnError: true + condition: always() + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: false + args: + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log' + artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} + displayName: 'Publish logs' + continueOnError: true + condition: always() + + - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: false + args: + displayName: Publish Logs + pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + publishLocation: Container + artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: false + args: + targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' + artifactName: 'BuildConfiguration' + displayName: 'Publish build retry configuration' + continueOnError: true diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml new file mode 100644 index 000000000..ff829dc4c --- /dev/null +++ b/eng/common/templates/job/onelocbuild.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/onelocbuild.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml new file mode 100644 index 000000000..ab2edec2a --- /dev/null +++ b/eng/common/templates/job/publish-build-assets.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/publish-build-assets.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml new file mode 100644 index 000000000..e44d47b1d --- /dev/null +++ b/eng/common/templates/job/source-build.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml new file mode 100644 index 000000000..89f329159 --- /dev/null +++ b/eng/common/templates/job/source-index-stage1.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/job/source-index-stage1.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/codeql-build.yml b/eng/common/templates/jobs/codeql-build.yml new file mode 100644 index 000000000..517f24d6a --- /dev/null +++ b/eng/common/templates/jobs/codeql-build.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/jobs/codeql-build.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml new file mode 100644 index 000000000..388e9037b --- /dev/null +++ b/eng/common/templates/jobs/jobs.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/source-build.yml b/eng/common/templates/jobs/source-build.yml new file mode 100644 index 000000000..818d4c326 --- /dev/null +++ b/eng/common/templates/jobs/source-build.yml @@ -0,0 +1,7 @@ +jobs: +- template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml new file mode 100644 index 000000000..7fa105875 --- /dev/null +++ b/eng/common/templates/post-build/common-variables.yml @@ -0,0 +1,8 @@ +variables: +- template: /eng/common/core-templates/post-build/common-variables.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml new file mode 100644 index 000000000..53ede714b --- /dev/null +++ b/eng/common/templates/post-build/post-build.yml @@ -0,0 +1,8 @@ +stages: +- template: /eng/common/core-templates/post-build/post-build.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/setup-maestro-vars.yml b/eng/common/templates/post-build/setup-maestro-vars.yml new file mode 100644 index 000000000..a79fab5b4 --- /dev/null +++ b/eng/common/templates/post-build/setup-maestro-vars.yml @@ -0,0 +1,8 @@ +steps: +- template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/trigger-subscription.yml b/eng/common/templates/post-build/trigger-subscription.yml new file mode 100644 index 000000000..da669030d --- /dev/null +++ b/eng/common/templates/post-build/trigger-subscription.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Triggering subscriptions + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/trigger-subscriptions.ps1 + arguments: -SourceRepo $(Build.Repository.Uri) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/templates/steps/add-build-to-channel.yml b/eng/common/templates/steps/add-build-to-channel.yml new file mode 100644 index 000000000..42bbba161 --- /dev/null +++ b/eng/common/templates/steps/add-build-to-channel.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/add-build-to-channel.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml new file mode 100644 index 000000000..c12a5f8d2 --- /dev/null +++ b/eng/common/templates/steps/component-governance.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/generate-sbom.yml b/eng/common/templates/steps/generate-sbom.yml new file mode 100644 index 000000000..26dc00a2e --- /dev/null +++ b/eng/common/templates/steps/generate-sbom.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/publish-build-artifacts.yml b/eng/common/templates/steps/publish-build-artifacts.yml new file mode 100644 index 000000000..6428a98df --- /dev/null +++ b/eng/common/templates/steps/publish-build-artifacts.yml @@ -0,0 +1,40 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: displayName + type: string + default: 'Publish to Build Artifact' + +- name: condition + type: string + default: succeeded() + +- name: artifactName + type: string + +- name: pathToPublish + type: string + +- name: continueOnError + type: boolean + default: false + +- name: publishLocation + type: string + default: 'Container' + +steps: +- ${{ if eq(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates cannot be referenced from a 1ES managed template': error +- task: PublishBuildArtifacts@1 + displayName: ${{ parameters.displayName }} + condition: ${{ parameters.condition }} + ${{ if parameters.continueOnError }}: + continueOnError: ${{ parameters.continueOnError }} + inputs: + PublishLocation: ${{ parameters.publishLocation }} + PathtoPublish: ${{ parameters.pathToPublish }} + ${{ if parameters.artifactName }}: + ArtifactName: ${{ parameters.artifactName }} \ No newline at end of file diff --git a/eng/common/templates/steps/publish-logs.yml b/eng/common/templates/steps/publish-logs.yml new file mode 100644 index 000000000..4ea86bd88 --- /dev/null +++ b/eng/common/templates/steps/publish-logs.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/publish-pipeline-artifacts.yml b/eng/common/templates/steps/publish-pipeline-artifacts.yml new file mode 100644 index 000000000..5dd698b21 --- /dev/null +++ b/eng/common/templates/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,34 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: args + type: object + default: {} + +steps: +- ${{ if eq(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates cannot be referenced from a 1ES managed template': error +- task: PublishPipelineArtifact@1 + displayName: ${{ coalesce(parameters.args.displayName, 'Publish to Build Artifact') }} + ${{ if parameters.args.condition }}: + condition: ${{ parameters.args.condition }} + ${{ else }}: + condition: succeeded() + ${{ if parameters.args.continueOnError }}: + continueOnError: ${{ parameters.args.continueOnError }} + inputs: + targetPath: ${{ parameters.args.targetPath }} + ${{ if parameters.args.artifactName }}: + artifactName: ${{ parameters.args.artifactName }} + ${{ if parameters.args.publishLocation }}: + publishLocation: ${{ parameters.args.publishLocation }} + ${{ if parameters.args.fileSharePath }}: + fileSharePath: ${{ parameters.args.fileSharePath }} + ${{ if parameters.args.Parallel }}: + parallel: ${{ parameters.args.Parallel }} + ${{ if parameters.args.parallelCount }}: + parallelCount: ${{ parameters.args.parallelCount }} + ${{ if parameters.args.properties }}: + properties: ${{ parameters.args.properties }} \ No newline at end of file diff --git a/eng/common/templates/steps/retain-build.yml b/eng/common/templates/steps/retain-build.yml new file mode 100644 index 000000000..8e841ace3 --- /dev/null +++ b/eng/common/templates/steps/retain-build.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/retain-build.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml new file mode 100644 index 000000000..39f99fc27 --- /dev/null +++ b/eng/common/templates/steps/send-to-helix.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/send-to-helix.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml new file mode 100644 index 000000000..23c1d6f4e --- /dev/null +++ b/eng/common/templates/steps/source-build.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/variables/pool-providers.yml b/eng/common/templates/variables/pool-providers.yml new file mode 100644 index 000000000..e0b19c14a --- /dev/null +++ b/eng/common/templates/variables/pool-providers.yml @@ -0,0 +1,59 @@ +# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool, +# otherwise it should go into the "normal" pools. This separates out the queueing and billing of released branches. + +# Motivation: +# Once a given branch of a repository's output has been officially "shipped" once, it is then considered to be COGS +# (Cost of goods sold) and should be moved to a servicing pool provider. This allows both separation of queueing +# (allowing release builds and main PR builds to not intefere with each other) and billing (required for COGS. +# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services +# team needs to move resources around and create new and potentially differently-named pools. Using this template +# file from an Arcade-ified repo helps guard against both having to update one's release/* branches and renaming. + +# How to use: +# This yaml assumes your shipped product branches use the naming convention "release/..." (which many do). +# If we find alternate naming conventions in broad usage it can be added to the condition below. +# +# First, import the template in an arcade-ified repo to pick up the variables, e.g.: +# +# variables: +# - template: /eng/common/templates/variables/pool-providers.yml +# +# ... then anywhere specifying the pool provider use the runtime variables, +# $(DncEngInternalBuildPool) and $ (DncEngPublicBuildPool), e.g.: +# +# pool: +# name: $(DncEngInternalBuildPool) +# demands: ImageOverride -equals windows.vs2019.amd64 +variables: + - ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - template: /eng/common/templates-official/variables/pool-providers.yml + - ${{ else }}: + # Coalesce the target and source branches so we know when a PR targets a release branch + # If these variables are somehow missing, fall back to main (tends to have more capacity) + + # Any new -Svc alternative pools should have variables added here to allow for splitting work + - name: DncEngPublicBuildPool + value: $[ + replace( + replace( + eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), + True, + 'NetCore-Svc-Public' + ), + False, + 'NetCore-Public' + ) + ] + + - name: DncEngInternalBuildPool + value: $[ + replace( + replace( + eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), + True, + 'NetCore1ESPool-Svc-Internal' + ), + False, + 'NetCore1ESPool-Internal' + ) + ] diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 new file mode 100644 index 000000000..0febe696d --- /dev/null +++ b/eng/common/tools.ps1 @@ -0,0 +1,965 @@ +# Initialize variables if they aren't already defined. +# These may be defined as parameters of the importing script, or set after importing this script. + +# CI mode - set to true on CI server for PR validation build or official build. +[bool]$ci = if (Test-Path variable:ci) { $ci } else { $false } + +# Build configuration. Common values include 'Debug' and 'Release', but the repository may use other names. +[string]$configuration = if (Test-Path variable:configuration) { $configuration } else { 'Debug' } + +# Set to true to opt out of outputting binary log while running in CI +[bool]$excludeCIBinarylog = if (Test-Path variable:excludeCIBinarylog) { $excludeCIBinarylog } else { $false } + +# Set to true to output binary log from msbuild. Note that emitting binary log slows down the build. +[bool]$binaryLog = if (Test-Path variable:binaryLog) { $binaryLog } else { $ci -and !$excludeCIBinarylog } + +# Set to true to use the pipelines logger which will enable Azure logging output. +# https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/authoring/commands.md +# This flag is meant as a temporary opt-opt for the feature while validate it across +# our consumers. It will be deleted in the future. +[bool]$pipelinesLog = if (Test-Path variable:pipelinesLog) { $pipelinesLog } else { $ci } + +# Turns on machine preparation/clean up code that changes the machine state (e.g. kills build processes). +[bool]$prepareMachine = if (Test-Path variable:prepareMachine) { $prepareMachine } else { $false } + +# True to restore toolsets and dependencies. +[bool]$restore = if (Test-Path variable:restore) { $restore } else { $true } + +# Adjusts msbuild verbosity level. +[string]$verbosity = if (Test-Path variable:verbosity) { $verbosity } else { 'minimal' } + +# Set to true to reuse msbuild nodes. Recommended to not reuse on CI. +[bool]$nodeReuse = if (Test-Path variable:nodeReuse) { $nodeReuse } else { !$ci } + +# Configures warning treatment in msbuild. +[bool]$warnAsError = if (Test-Path variable:warnAsError) { $warnAsError } else { $true } + +# Specifies which msbuild engine to use for build: 'vs', 'dotnet' or unspecified (determined based on presence of tools.vs in global.json). +[string]$msbuildEngine = if (Test-Path variable:msbuildEngine) { $msbuildEngine } else { $null } + +# True to attempt using .NET Core already that meets requirements specified in global.json +# installed on the machine instead of downloading one. +[bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true } + +# Enable repos to use a particular version of the on-line dotnet-install scripts. +# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1 +[string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { 'v1' } + +# True to use global NuGet cache instead of restoring packages to repository-local directory. +[bool]$useGlobalNuGetCache = if (Test-Path variable:useGlobalNuGetCache) { $useGlobalNuGetCache } else { !$ci } + +# True to exclude prerelease versions Visual Studio during build +[bool]$excludePrereleaseVS = if (Test-Path variable:excludePrereleaseVS) { $excludePrereleaseVS } else { $false } + +# An array of names of processes to stop on script exit if prepareMachine is true. +$processesToStopOnExit = if (Test-Path variable:processesToStopOnExit) { $processesToStopOnExit } else { @('msbuild', 'dotnet', 'vbcscompiler') } + +$disableConfigureToolsetImport = if (Test-Path variable:disableConfigureToolsetImport) { $disableConfigureToolsetImport } else { $null } + +set-strictmode -version 2.0 +$ErrorActionPreference = 'Stop' +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +# If specifies, provides an alternate path for getting .NET Core SDKs and Runtimes. This script will still try public sources first. +[string]$runtimeSourceFeed = if (Test-Path variable:runtimeSourceFeed) { $runtimeSourceFeed } else { $null } +# Base-64 encoded SAS token that has permission to storage container described by $runtimeSourceFeed +[string]$runtimeSourceFeedKey = if (Test-Path variable:runtimeSourceFeedKey) { $runtimeSourceFeedKey } else { $null } + +# True if the build is a product build +[bool]$productBuild = if (Test-Path variable:productBuild) { $productBuild } else { $false } + +[String[]]$properties = if (Test-Path variable:properties) { $properties } else { @() } + +function Create-Directory ([string[]] $path) { + New-Item -Path $path -Force -ItemType 'Directory' | Out-Null +} + +function Unzip([string]$zipfile, [string]$outpath) { + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath) +} + +# This will exec a process using the console and return it's exit code. +# This will not throw when the process fails. +# Returns process exit code. +function Exec-Process([string]$command, [string]$commandArgs) { + $startInfo = New-Object System.Diagnostics.ProcessStartInfo + $startInfo.FileName = $command + $startInfo.Arguments = $commandArgs + $startInfo.UseShellExecute = $false + $startInfo.WorkingDirectory = Get-Location + + $process = New-Object System.Diagnostics.Process + $process.StartInfo = $startInfo + $process.Start() | Out-Null + + $finished = $false + try { + while (-not $process.WaitForExit(100)) { + # Non-blocking loop done to allow ctr-c interrupts + } + + $finished = $true + return $global:LASTEXITCODE = $process.ExitCode + } + finally { + # If we didn't finish then an error occurred or the user hit ctrl-c. Either + # way kill the process + if (-not $finished) { + $process.Kill() + } + } +} + +# Take the given block, print it, print what the block probably references from the current set of +# variables using low-effort string matching, then run the block. +# +# This is intended to replace the pattern of manually copy-pasting a command, wrapping it in quotes, +# and printing it using "Write-Host". The copy-paste method is more readable in build logs, but less +# maintainable and less reliable. It is easy to make a mistake and modify the command without +# properly updating the "Write-Host" line, resulting in misleading build logs. The probability of +# this mistake makes the pattern hard to trust when it shows up in build logs. Finding the bug in +# existing source code can also be difficult, because the strings are not aligned to each other and +# the line may be 300+ columns long. +# +# By removing the need to maintain two copies of the command, Exec-BlockVerbosely avoids the issues. +# +# In Bash (or any posix-like shell), "set -x" prints usable verbose output automatically. +# "Set-PSDebug" appears to be similar at first glance, but unfortunately, it isn't very useful: it +# doesn't print any info about the variables being used by the command, which is normally the +# interesting part to diagnose. +function Exec-BlockVerbosely([scriptblock] $block) { + Write-Host "--- Running script block:" + $blockString = $block.ToString().Trim() + Write-Host $blockString + + Write-Host "--- List of variables that might be used:" + # For each variable x in the environment, check the block for a reference to x via simple "$x" or + # "@x" syntax. This doesn't detect other ways to reference variables ("${x}" nor "$variable:x", + # among others). It only catches what this function was originally written for: simple + # command-line commands. + $variableTable = Get-Variable | + Where-Object { + $blockString.Contains("`$$($_.Name)") -or $blockString.Contains("@$($_.Name)") + } | + Format-Table -AutoSize -HideTableHeaders -Wrap | + Out-String + Write-Host $variableTable.Trim() + + Write-Host "--- Executing:" + & $block + Write-Host "--- Done running script block!" +} + +# createSdkLocationFile parameter enables a file being generated under the toolset directory +# which writes the sdk's location into. This is only necessary for cmd --> powershell invocations +# as dot sourcing isn't possible. +function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { + if (Test-Path variable:global:_DotNetInstallDir) { + return $global:_DotNetInstallDir + } + + # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism + $env:DOTNET_MULTILEVEL_LOOKUP=0 + + # Disable first run since we do not need all ASP.NET packages restored. + $env:DOTNET_NOLOGO=1 + + # Disable telemetry on CI. + if ($ci) { + $env:DOTNET_CLI_TELEMETRY_OPTOUT=1 + } + + # Find the first path on %PATH% that contains the dotnet.exe + if ($useInstalledDotNetCli -and (-not $globalJsonHasRuntimes) -and ($env:DOTNET_INSTALL_DIR -eq $null)) { + $dotnetExecutable = GetExecutableFileName 'dotnet' + $dotnetCmd = Get-Command $dotnetExecutable -ErrorAction SilentlyContinue + + if ($dotnetCmd -ne $null) { + $env:DOTNET_INSTALL_DIR = Split-Path $dotnetCmd.Path -Parent + } + } + + $dotnetSdkVersion = $GlobalJson.tools.dotnet + + # Use dotnet installation specified in DOTNET_INSTALL_DIR if it contains the required SDK version, + # otherwise install the dotnet CLI and SDK to repo local .dotnet directory to avoid potential permission issues. + if ((-not $globalJsonHasRuntimes) -and (-not [string]::IsNullOrEmpty($env:DOTNET_INSTALL_DIR)) -and (Test-Path(Join-Path $env:DOTNET_INSTALL_DIR "sdk\$dotnetSdkVersion"))) { + $dotnetRoot = $env:DOTNET_INSTALL_DIR + } else { + $dotnetRoot = Join-Path $RepoRoot '.dotnet' + + if (-not (Test-Path(Join-Path $dotnetRoot "sdk\$dotnetSdkVersion"))) { + if ($install) { + InstallDotNetSdk $dotnetRoot $dotnetSdkVersion + } else { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Unable to find dotnet with SDK version '$dotnetSdkVersion'" + ExitWithExitCode 1 + } + } + + $env:DOTNET_INSTALL_DIR = $dotnetRoot + } + + # Creates a temporary file under the toolset dir. + # The following code block is protecting against concurrent access so that this function can + # be called in parallel. + if ($createSdkLocationFile) { + do { + $sdkCacheFileTemp = Join-Path $ToolsetDir $([System.IO.Path]::GetRandomFileName()) + } + until (!(Test-Path $sdkCacheFileTemp)) + Set-Content -Path $sdkCacheFileTemp -Value $dotnetRoot + + try { + Move-Item -Force $sdkCacheFileTemp (Join-Path $ToolsetDir 'sdk.txt') + } catch { + # Somebody beat us + Remove-Item -Path $sdkCacheFileTemp + } + } + + # Add dotnet to PATH. This prevents any bare invocation of dotnet in custom + # build steps from using anything other than what we've downloaded. + # It also ensures that VS msbuild will use the downloaded sdk targets. + $env:PATH = "$dotnetRoot;$env:PATH" + + # Make Sure that our bootstrapped dotnet cli is available in future steps of the Azure Pipelines build + Write-PipelinePrependPath -Path $dotnetRoot + + Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0' + Write-PipelineSetVariable -Name 'DOTNET_NOLOGO' -Value '1' + + return $global:_DotNetInstallDir = $dotnetRoot +} + +function Retry($downloadBlock, $maxRetries = 5) { + $retries = 1 + + while($true) { + try { + & $downloadBlock + break + } + catch { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ + } + + if (++$retries -le $maxRetries) { + $delayInSeconds = [math]::Pow(2, $retries) - 1 # Exponential backoff + Write-Host "Retrying. Waiting for $delayInSeconds seconds before next attempt ($retries of $maxRetries)." + Start-Sleep -Seconds $delayInSeconds + } + else { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Unable to download file in $maxRetries attempts." + break + } + } +} + +function GetDotNetInstallScript([string] $dotnetRoot) { + $installScript = Join-Path $dotnetRoot 'dotnet-install.ps1' + if (!(Test-Path $installScript)) { + Create-Directory $dotnetRoot + $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit + $uri = "https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1" + + Retry({ + Write-Host "GET $uri" + Invoke-WebRequest $uri -OutFile $installScript + }) + } + + return $installScript +} + +function InstallDotNetSdk([string] $dotnetRoot, [string] $version, [string] $architecture = '', [switch] $noPath) { + InstallDotNet $dotnetRoot $version $architecture '' $false $runtimeSourceFeed $runtimeSourceFeedKey -noPath:$noPath +} + +function InstallDotNet([string] $dotnetRoot, + [string] $version, + [string] $architecture = '', + [string] $runtime = '', + [bool] $skipNonVersionedFiles = $false, + [string] $runtimeSourceFeed = '', + [string] $runtimeSourceFeedKey = '', + [switch] $noPath) { + + $dotnetVersionLabel = "'sdk v$version'" + + if ($runtime -ne '' -and $runtime -ne 'sdk') { + $runtimePath = $dotnetRoot + $runtimePath = $runtimePath + "\shared" + if ($runtime -eq "dotnet") { $runtimePath = $runtimePath + "\Microsoft.NETCore.App" } + if ($runtime -eq "aspnetcore") { $runtimePath = $runtimePath + "\Microsoft.AspNetCore.App" } + if ($runtime -eq "windowsdesktop") { $runtimePath = $runtimePath + "\Microsoft.WindowsDesktop.App" } + $runtimePath = $runtimePath + "\" + $version + + $dotnetVersionLabel = "runtime toolset '$runtime/$architecture v$version'" + + if (Test-Path $runtimePath) { + Write-Host " Runtime toolset '$runtime/$architecture v$version' already installed." + $installSuccess = $true + Exit + } + } + + $installScript = GetDotNetInstallScript $dotnetRoot + $installParameters = @{ + Version = $version + InstallDir = $dotnetRoot + } + + if ($architecture) { $installParameters.Architecture = $architecture } + if ($runtime) { $installParameters.Runtime = $runtime } + if ($skipNonVersionedFiles) { $installParameters.SkipNonVersionedFiles = $skipNonVersionedFiles } + if ($noPath) { $installParameters.NoPath = $True } + + $variations = @() + $variations += @($installParameters) + + $dotnetBuilds = $installParameters.Clone() + $dotnetbuilds.AzureFeed = "https://dotnetbuilds.azureedge.net/public" + $variations += @($dotnetBuilds) + + if ($runtimeSourceFeed) { + $runtimeSource = $installParameters.Clone() + $runtimeSource.AzureFeed = $runtimeSourceFeed + if ($runtimeSourceFeedKey) { + $decodedBytes = [System.Convert]::FromBase64String($runtimeSourceFeedKey) + $decodedString = [System.Text.Encoding]::UTF8.GetString($decodedBytes) + $runtimeSource.FeedCredential = $decodedString + } + $variations += @($runtimeSource) + } + + $installSuccess = $false + foreach ($variation in $variations) { + if ($variation | Get-Member AzureFeed) { + $location = $variation.AzureFeed + } else { + $location = "public location"; + } + Write-Host " Attempting to install $dotnetVersionLabel from $location." + try { + & $installScript @variation + $installSuccess = $true + break + } + catch { + Write-Host " Failed to install $dotnetVersionLabel from $location." + } + } + if (-not $installSuccess) { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Failed to install $dotnetVersionLabel from any of the specified locations." + ExitWithExitCode 1 + } +} + +# +# Locates Visual Studio MSBuild installation. +# The preference order for MSBuild to use is as follows: +# +# 1. MSBuild from an active VS command prompt +# 2. MSBuild from a compatible VS installation +# 3. MSBuild from the xcopy tool package +# +# Returns full path to msbuild.exe. +# Throws on failure. +# +function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = $null) { + if (-not (IsWindowsPlatform)) { + throw "Cannot initialize Visual Studio on non-Windows" + } + + if (Test-Path variable:global:_MSBuildExe) { + return $global:_MSBuildExe + } + + # Minimum VS version to require. + $vsMinVersionReqdStr = '17.7' + $vsMinVersionReqd = [Version]::new($vsMinVersionReqdStr) + + # If the version of msbuild is going to be xcopied, + # use this version. Version matches a package here: + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.10.0-pre.4.0 + $defaultXCopyMSBuildVersion = '17.10.0-pre.4.0' + + if (!$vsRequirements) { + if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { + $vsRequirements = $GlobalJson.tools.vs + } + else { + $vsRequirements = New-Object PSObject -Property @{ version = $vsMinVersionReqdStr } + } + } + $vsMinVersionStr = if ($vsRequirements.version) { $vsRequirements.version } else { $vsMinVersionReqdStr } + $vsMinVersion = [Version]::new($vsMinVersionStr) + + # Try msbuild command available in the environment. + if ($env:VSINSTALLDIR -ne $null) { + $msbuildCmd = Get-Command 'msbuild.exe' -ErrorAction SilentlyContinue + if ($msbuildCmd -ne $null) { + # Workaround for https://github.com/dotnet/roslyn/issues/35793 + # Due to this issue $msbuildCmd.Version returns 0.0.0.0 for msbuild.exe 16.2+ + $msbuildVersion = [Version]::new((Get-Item $msbuildCmd.Path).VersionInfo.ProductVersion.Split([char[]]@('-', '+'))[0]) + + if ($msbuildVersion -ge $vsMinVersion) { + return $global:_MSBuildExe = $msbuildCmd.Path + } + + # Report error - the developer environment is initialized with incompatible VS version. + throw "Developer Command Prompt for VS $($env:VisualStudioVersion) is not recent enough. Please upgrade to $vsMinVersionStr or build from a plain CMD window" + } + } + + # Locate Visual Studio installation or download x-copy msbuild. + $vsInfo = LocateVisualStudio $vsRequirements + if ($vsInfo -ne $null) { + # Ensure vsInstallDir has a trailing slash + $vsInstallDir = Join-Path $vsInfo.installationPath "\" + $vsMajorVersion = $vsInfo.installationVersion.Split('.')[0] + + InitializeVisualStudioEnvironmentVariables $vsInstallDir $vsMajorVersion + } else { + if (Get-Member -InputObject $GlobalJson.tools -Name 'xcopy-msbuild') { + $xcopyMSBuildVersion = $GlobalJson.tools.'xcopy-msbuild' + $vsMajorVersion = $xcopyMSBuildVersion.Split('.')[0] + } else { + #if vs version provided in global.json is incompatible (too low) then use the default version for xcopy msbuild download + if($vsMinVersion -lt $vsMinVersionReqd){ + Write-Host "Using xcopy-msbuild version of $defaultXCopyMSBuildVersion since VS version $vsMinVersionStr provided in global.json is not compatible" + $xcopyMSBuildVersion = $defaultXCopyMSBuildVersion + $vsMajorVersion = $xcopyMSBuildVersion.Split('.')[0] + } + else{ + # If the VS version IS compatible, look for an xcopy msbuild package + # with a version matching VS. + # Note: If this version does not exist, then an explicit version of xcopy msbuild + # can be specified in global.json. This will be required for pre-release versions of msbuild. + $vsMajorVersion = $vsMinVersion.Major + $vsMinorVersion = $vsMinVersion.Minor + $xcopyMSBuildVersion = "$vsMajorVersion.$vsMinorVersion.0" + } + } + + $vsInstallDir = $null + if ($xcopyMSBuildVersion.Trim() -ine "none") { + $vsInstallDir = InitializeXCopyMSBuild $xcopyMSBuildVersion $install + if ($vsInstallDir -eq $null) { + throw "Could not xcopy msbuild. Please check that package 'Microsoft.DotNet.Arcade.MSBuild.Xcopy @ $xcopyMSBuildVersion' exists on feed 'dotnet-eng'." + } + } + if ($vsInstallDir -eq $null) { + throw 'Unable to find Visual Studio that has required version and components installed' + } + } + + $msbuildVersionDir = if ([int]$vsMajorVersion -lt 16) { "$vsMajorVersion.0" } else { "Current" } + + $local:BinFolder = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin" + $local:Prefer64bit = if (Get-Member -InputObject $vsRequirements -Name 'Prefer64bit') { $vsRequirements.Prefer64bit } else { $false } + if ($local:Prefer64bit -and (Test-Path(Join-Path $local:BinFolder "amd64"))) { + $global:_MSBuildExe = Join-Path $local:BinFolder "amd64\msbuild.exe" + } else { + $global:_MSBuildExe = Join-Path $local:BinFolder "msbuild.exe" + } + + return $global:_MSBuildExe +} + +function InitializeVisualStudioEnvironmentVariables([string] $vsInstallDir, [string] $vsMajorVersion) { + $env:VSINSTALLDIR = $vsInstallDir + Set-Item "env:VS$($vsMajorVersion)0COMNTOOLS" (Join-Path $vsInstallDir "Common7\Tools\") + + $vsSdkInstallDir = Join-Path $vsInstallDir "VSSDK\" + if (Test-Path $vsSdkInstallDir) { + Set-Item "env:VSSDK$($vsMajorVersion)0Install" $vsSdkInstallDir + $env:VSSDKInstall = $vsSdkInstallDir + } +} + +function InstallXCopyMSBuild([string]$packageVersion) { + return InitializeXCopyMSBuild $packageVersion -install $true +} + +function InitializeXCopyMSBuild([string]$packageVersion, [bool]$install) { + $packageName = 'Microsoft.DotNet.Arcade.MSBuild.Xcopy' + $packageDir = Join-Path $ToolsDir "msbuild\$packageVersion" + $packagePath = Join-Path $packageDir "$packageName.$packageVersion.nupkg" + + if (!(Test-Path $packageDir)) { + if (!$install) { + return $null + } + + Create-Directory $packageDir + + Write-Host "Downloading $packageName $packageVersion" + $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit + Retry({ + Invoke-WebRequest "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/flat2/$packageName/$packageVersion/$packageName.$packageVersion.nupkg" -OutFile $packagePath + }) + + if (!(Test-Path $packagePath)) { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "See https://dev.azure.com/dnceng/internal/_wiki/wikis/DNCEng%20Services%20Wiki/1074/Updating-Microsoft.DotNet.Arcade.MSBuild.Xcopy-WAS-RoslynTools.MSBuild-(xcopy-msbuild)-generation?anchor=troubleshooting for help troubleshooting issues with XCopy MSBuild" + throw + } + Unzip $packagePath $packageDir + } + + return Join-Path $packageDir 'tools' +} + +# +# Locates Visual Studio instance that meets the minimal requirements specified by tools.vs object in global.json. +# +# The following properties of tools.vs are recognized: +# "version": "{major}.{minor}" +# Two part minimal VS version, e.g. "15.9", "16.0", etc. +# "components": ["componentId1", "componentId2", ...] +# Array of ids of workload components that must be available in the VS instance. +# See e.g. https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-enterprise?view=vs-2017 +# +# Returns JSON describing the located VS instance (same format as returned by vswhere), +# or $null if no instance meeting the requirements is found on the machine. +# +function LocateVisualStudio([object]$vsRequirements = $null){ + if (-not (IsWindowsPlatform)) { + throw "Cannot run vswhere on non-Windows platforms." + } + + if (Get-Member -InputObject $GlobalJson.tools -Name 'vswhere') { + $vswhereVersion = $GlobalJson.tools.vswhere + } else { + $vswhereVersion = '2.5.2' + } + + $vsWhereDir = Join-Path $ToolsDir "vswhere\$vswhereVersion" + $vsWhereExe = Join-Path $vsWhereDir 'vswhere.exe' + + if (!(Test-Path $vsWhereExe)) { + Create-Directory $vsWhereDir + Write-Host 'Downloading vswhere' + Retry({ + Invoke-WebRequest "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/vswhere/$vswhereVersion/vswhere.exe" -OutFile $vswhereExe + }) + } + + if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs } + $args = @('-latest', '-format', 'json', '-requires', 'Microsoft.Component.MSBuild', '-products', '*') + + if (!$excludePrereleaseVS) { + $args += '-prerelease' + } + + if (Get-Member -InputObject $vsRequirements -Name 'version') { + $args += '-version' + $args += $vsRequirements.version + } + + if (Get-Member -InputObject $vsRequirements -Name 'components') { + foreach ($component in $vsRequirements.components) { + $args += '-requires' + $args += $component + } + } + + $vsInfo =& $vsWhereExe $args | ConvertFrom-Json + + if ($lastExitCode -ne 0) { + return $null + } + + # use first matching instance + return $vsInfo[0] +} + +function InitializeBuildTool() { + if (Test-Path variable:global:_BuildTool) { + # If the requested msbuild parameters do not match, clear the cached variables. + if($global:_BuildTool.Contains('ExcludePrereleaseVS') -and $global:_BuildTool.ExcludePrereleaseVS -ne $excludePrereleaseVS) { + Remove-Item variable:global:_BuildTool + Remove-Item variable:global:_MSBuildExe + } else { + return $global:_BuildTool + } + } + + if (-not $msbuildEngine) { + $msbuildEngine = GetDefaultMSBuildEngine + } + + # Initialize dotnet cli if listed in 'tools' + $dotnetRoot = $null + if (Get-Member -InputObject $GlobalJson.tools -Name 'dotnet') { + $dotnetRoot = InitializeDotNetCli -install:$restore + } + + if ($msbuildEngine -eq 'dotnet') { + if (!$dotnetRoot) { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "/global.json must specify 'tools.dotnet'." + ExitWithExitCode 1 + } + $dotnetPath = Join-Path $dotnetRoot (GetExecutableFileName 'dotnet') + + # Use override if it exists - commonly set by source-build + if ($null -eq $env:_OverrideArcadeInitializeBuildToolFramework) { + $initializeBuildToolFramework="net9.0" + } else { + $initializeBuildToolFramework=$env:_OverrideArcadeInitializeBuildToolFramework + } + + $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = $initializeBuildToolFramework } + } elseif ($msbuildEngine -eq "vs") { + try { + $msbuildPath = InitializeVisualStudioMSBuild -install:$restore + } catch { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ + ExitWithExitCode 1 + } + + $buildTool = @{ Path = $msbuildPath; Command = ""; Tool = "vs"; Framework = "net472"; ExcludePrereleaseVS = $excludePrereleaseVS } + } else { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Unexpected value of -msbuildEngine: '$msbuildEngine'." + ExitWithExitCode 1 + } + + return $global:_BuildTool = $buildTool +} + +function GetDefaultMSBuildEngine() { + # Presence of tools.vs indicates the repo needs to build using VS msbuild on Windows. + if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { + return 'vs' + } + + if (Get-Member -InputObject $GlobalJson.tools -Name 'dotnet') { + return 'dotnet' + } + + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "-msbuildEngine must be specified, or /global.json must specify 'tools.dotnet' or 'tools.vs'." + ExitWithExitCode 1 +} + +function GetNuGetPackageCachePath() { + if ($env:NUGET_PACKAGES -eq $null) { + # Use local cache on CI to ensure deterministic build. + # Avoid using the http cache as workaround for https://github.com/NuGet/Home/issues/3116 + # use global cache in dev builds to avoid cost of downloading packages. + # For directory normalization, see also: https://github.com/NuGet/Home/issues/7968 + if ($useGlobalNuGetCache) { + $env:NUGET_PACKAGES = Join-Path $env:UserProfile '.nuget\packages\' + } else { + $env:NUGET_PACKAGES = Join-Path $RepoRoot '.packages\' + $env:RESTORENOCACHE = $true + } + } + + return $env:NUGET_PACKAGES +} + +# Returns a full path to an Arcade SDK task project file. +function GetSdkTaskProject([string]$taskName) { + return Join-Path (Split-Path (InitializeToolset) -Parent) "SdkTasks\$taskName.proj" +} + +function InitializeNativeTools() { + if (-Not (Test-Path variable:DisableNativeToolsetInstalls) -And (Get-Member -InputObject $GlobalJson -Name "native-tools")) { + $nativeArgs= @{} + if ($ci) { + $nativeArgs = @{ + InstallDirectory = "$ToolsDir" + } + } + if ($env:NativeToolsOnMachine) { + Write-Host "Variable NativeToolsOnMachine detected, enabling native tool path promotion..." + $nativeArgs += @{ PathPromotion = $true } + } + & "$PSScriptRoot/init-tools-native.ps1" @nativeArgs + } +} + +function Read-ArcadeSdkVersion() { + return $GlobalJson.'msbuild-sdks'.'Microsoft.DotNet.Arcade.Sdk' +} + +function InitializeToolset() { + # For Unified Build/Source-build support, check whether the environment variable is + # set. If it is, then use this as the toolset build project. + if ($env:_InitializeToolset -ne $null) { + return $global:_InitializeToolset = $env:_InitializeToolset + } + + if (Test-Path variable:global:_InitializeToolset) { + return $global:_InitializeToolset + } + + $nugetCache = GetNuGetPackageCachePath + + $toolsetVersion = Read-ArcadeSdkVersion + $toolsetLocationFile = Join-Path $ToolsetDir "$toolsetVersion.txt" + + if (Test-Path $toolsetLocationFile) { + $path = Get-Content $toolsetLocationFile -TotalCount 1 + if (Test-Path $path) { + return $global:_InitializeToolset = $path + } + } + + if (-not $restore) { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Toolset version $toolsetVersion has not been restored." + ExitWithExitCode 1 + } + + $buildTool = InitializeBuildTool + + $proj = Join-Path $ToolsetDir 'restore.proj' + $bl = if ($binaryLog) { '/bl:' + (Join-Path $LogDir 'ToolsetRestore.binlog') } else { '' } + + '' | Set-Content $proj + + MSBuild-Core $proj $bl /t:__WriteToolsetLocation /clp:ErrorsOnly`;NoSummary /p:__ToolsetLocationOutputFile=$toolsetLocationFile + + $path = Get-Content $toolsetLocationFile -Encoding UTF8 -TotalCount 1 + if (!(Test-Path $path)) { + throw "Invalid toolset path: $path" + } + + return $global:_InitializeToolset = $path +} + +function ExitWithExitCode([int] $exitCode) { + if ($ci -and $prepareMachine) { + Stop-Processes + } + exit $exitCode +} + +# Check if $LASTEXITCODE is a nonzero exit code (NZEC). If so, print a Azure Pipeline error for +# diagnostics, then exit the script with the $LASTEXITCODE. +function Exit-IfNZEC([string] $category = "General") { + Write-Host "Exit code $LASTEXITCODE" + if ($LASTEXITCODE -ne 0) { + $message = "Last command failed with exit code $LASTEXITCODE." + Write-PipelineTelemetryError -Force -Category $category -Message $message + ExitWithExitCode $LASTEXITCODE + } +} + +function Stop-Processes() { + Write-Host 'Killing running build processes...' + foreach ($processName in $processesToStopOnExit) { + Get-Process -Name $processName -ErrorAction SilentlyContinue | Stop-Process + } +} + +# +# Executes msbuild (or 'dotnet msbuild') with arguments passed to the function. +# The arguments are automatically quoted. +# Terminates the script if the build fails. +# +function MSBuild() { + if ($pipelinesLog) { + $buildTool = InitializeBuildTool + + if ($ci -and $buildTool.Tool -eq 'dotnet') { + $env:NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS = 20 + $env:NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS = 20 + Write-PipelineSetVariable -Name 'NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS' -Value '20' + Write-PipelineSetVariable -Name 'NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS' -Value '20' + } + + Enable-Nuget-EnhancedRetry + + $toolsetBuildProject = InitializeToolset + $basePath = Split-Path -parent $toolsetBuildProject + $possiblePaths = @( + # new scripts need to work with old packages, so we need to look for the old names/versions + (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.ArcadeLogging.dll')), + (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.Arcade.Sdk.dll')), + (Join-Path $basePath (Join-Path net7.0 'Microsoft.DotNet.ArcadeLogging.dll')), + (Join-Path $basePath (Join-Path net7.0 'Microsoft.DotNet.Arcade.Sdk.dll')), + (Join-Path $basePath (Join-Path net8.0 'Microsoft.DotNet.ArcadeLogging.dll')), + (Join-Path $basePath (Join-Path net8.0 'Microsoft.DotNet.Arcade.Sdk.dll')) + ) + $selectedPath = $null + foreach ($path in $possiblePaths) { + if (Test-Path $path -PathType Leaf) { + $selectedPath = $path + break + } + } + if (-not $selectedPath) { + Write-PipelineTelemetryError -Category 'Build' -Message 'Unable to find arcade sdk logger assembly.' + ExitWithExitCode 1 + } + $args += "/logger:$selectedPath" + } + + MSBuild-Core @args +} + +# +# Executes msbuild (or 'dotnet msbuild') with arguments passed to the function. +# The arguments are automatically quoted. +# Terminates the script if the build fails. +# +function MSBuild-Core() { + if ($ci) { + if (!$binaryLog -and !$excludeCIBinarylog) { + Write-PipelineTelemetryError -Category 'Build' -Message 'Binary log must be enabled in CI build, or explicitly opted-out from with the -excludeCIBinarylog switch.' + ExitWithExitCode 1 + } + + if ($nodeReuse) { + Write-PipelineTelemetryError -Category 'Build' -Message 'Node reuse must be disabled in CI build.' + ExitWithExitCode 1 + } + } + + Enable-Nuget-EnhancedRetry + + $buildTool = InitializeBuildTool + + $cmdArgs = "$($buildTool.Command) /m /nologo /clp:Summary /v:$verbosity /nr:$nodeReuse /p:ContinuousIntegrationBuild=$ci" + + if ($warnAsError) { + $cmdArgs += ' /warnaserror /p:TreatWarningsAsErrors=true' + } + else { + $cmdArgs += ' /p:TreatWarningsAsErrors=false' + } + + foreach ($arg in $args) { + if ($null -ne $arg -and $arg.Trim() -ne "") { + if ($arg.EndsWith('\')) { + $arg = $arg + "\" + } + $cmdArgs += " `"$arg`"" + } + } + + # Be sure quote the path in case there are spaces in the dotnet installation location. + $env:ARCADE_BUILD_TOOL_COMMAND = "`"$($buildTool.Path)`" $cmdArgs" + + $exitCode = Exec-Process $buildTool.Path $cmdArgs + + if ($exitCode -ne 0) { + # We should not Write-PipelineTaskError here because that message shows up in the build summary + # The build already logged an error, that's the reason it failed. Producing an error here only adds noise. + Write-Host "Build failed with exit code $exitCode. Check errors above." -ForegroundColor Red + + $buildLog = GetMSBuildBinaryLogCommandLineArgument $args + if ($null -ne $buildLog) { + Write-Host "See log: $buildLog" -ForegroundColor DarkGray + } + + # When running on Azure Pipelines, override the returned exit code to avoid double logging. + # Skip this when the build is a child of the VMR orchestrator build. + if ($ci -and $env:SYSTEM_TEAMPROJECT -ne $null -and !$productBuild -and -not($properties -like "*DotNetBuildRepo=true*")) { + Write-PipelineSetResult -Result "Failed" -Message "msbuild execution failed." + # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error + # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error + ExitWithExitCode 0 + } else { + ExitWithExitCode $exitCode + } + } +} + +function GetMSBuildBinaryLogCommandLineArgument($arguments) { + foreach ($argument in $arguments) { + if ($argument -ne $null) { + $arg = $argument.Trim() + if ($arg.StartsWith('/bl:', "OrdinalIgnoreCase")) { + return $arg.Substring('/bl:'.Length) + } + + if ($arg.StartsWith('/binaryLogger:', 'OrdinalIgnoreCase')) { + return $arg.Substring('/binaryLogger:'.Length) + } + } + } + + return $null +} + +function GetExecutableFileName($baseName) { + if (IsWindowsPlatform) { + return "$baseName.exe" + } + else { + return $baseName + } +} + +function IsWindowsPlatform() { + return [environment]::OSVersion.Platform -eq [PlatformID]::Win32NT +} + +function Get-Darc($version) { + $darcPath = "$TempDir\darc\$(New-Guid)" + if ($version -ne $null) { + & $PSScriptRoot\darc-init.ps1 -toolpath $darcPath -darcVersion $version | Out-Host + } else { + & $PSScriptRoot\darc-init.ps1 -toolpath $darcPath | Out-Host + } + return "$darcPath\darc.exe" +} + +. $PSScriptRoot\pipeline-logging-functions.ps1 + +$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..\') +$EngRoot = Resolve-Path (Join-Path $PSScriptRoot '..') +$ArtifactsDir = Join-Path $RepoRoot 'artifacts' +$ToolsetDir = Join-Path $ArtifactsDir 'toolset' +$ToolsDir = Join-Path $RepoRoot '.tools' +$LogDir = Join-Path (Join-Path $ArtifactsDir 'log') $configuration +$TempDir = Join-Path (Join-Path $ArtifactsDir 'tmp') $configuration +$GlobalJson = Get-Content -Raw -Path (Join-Path $RepoRoot 'global.json') | ConvertFrom-Json +# true if global.json contains a "runtimes" section +$globalJsonHasRuntimes = if ($GlobalJson.tools.PSObject.Properties.Name -Match 'runtimes') { $true } else { $false } + +Create-Directory $ToolsetDir +Create-Directory $TempDir +Create-Directory $LogDir + +Write-PipelineSetVariable -Name 'Artifacts' -Value $ArtifactsDir +Write-PipelineSetVariable -Name 'Artifacts.Toolset' -Value $ToolsetDir +Write-PipelineSetVariable -Name 'Artifacts.Log' -Value $LogDir +Write-PipelineSetVariable -Name 'TEMP' -Value $TempDir +Write-PipelineSetVariable -Name 'TMP' -Value $TempDir + +# Import custom tools configuration, if present in the repo. +# Note: Import in global scope so that the script set top-level variables without qualification. +if (!$disableConfigureToolsetImport) { + $configureToolsetScript = Join-Path $EngRoot 'configure-toolset.ps1' + if (Test-Path $configureToolsetScript) { + . $configureToolsetScript + if ((Test-Path variable:failOnConfigureToolsetError) -And $failOnConfigureToolsetError) { + if ((Test-Path variable:LastExitCode) -And ($LastExitCode -ne 0)) { + Write-PipelineTelemetryError -Category 'Build' -Message 'configure-toolset.ps1 returned a non-zero exit code' + ExitWithExitCode $LastExitCode + } + } + } +} + +# +# If $ci flag is set, turn on (and log that we did) special environment variables for improved Nuget client retry logic. +# +function Enable-Nuget-EnhancedRetry() { + if ($ci) { + Write-Host "Setting NUGET enhanced retry environment variables" + $env:NUGET_ENABLE_ENHANCED_HTTP_RETRY = 'true' + $env:NUGET_ENHANCED_MAX_NETWORK_TRY_COUNT = 6 + $env:NUGET_ENHANCED_NETWORK_RETRY_DELAY_MILLISECONDS = 1000 + $env:NUGET_RETRY_HTTP_429 = 'true' + Write-PipelineSetVariable -Name 'NUGET_ENABLE_ENHANCED_HTTP_RETRY' -Value 'true' + Write-PipelineSetVariable -Name 'NUGET_ENHANCED_MAX_NETWORK_TRY_COUNT' -Value '6' + Write-PipelineSetVariable -Name 'NUGET_ENHANCED_NETWORK_RETRY_DELAY_MILLISECONDS' -Value '1000' + Write-PipelineSetVariable -Name 'NUGET_RETRY_HTTP_429' -Value 'true' + } +} diff --git a/eng/common/tools.sh b/eng/common/tools.sh new file mode 100755 index 000000000..db64e298f --- /dev/null +++ b/eng/common/tools.sh @@ -0,0 +1,589 @@ +#!/usr/bin/env bash + +# Initialize variables if they aren't already defined. + +# CI mode - set to true on CI server for PR validation build or official build. +ci=${ci:-false} + +# Set to true to use the pipelines logger which will enable Azure logging output. +# https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/authoring/commands.md +# This flag is meant as a temporary opt-opt for the feature while validate it across +# our consumers. It will be deleted in the future. +if [[ "$ci" == true ]]; then + pipelines_log=${pipelines_log:-true} +else + pipelines_log=${pipelines_log:-false} +fi + +# Build configuration. Common values include 'Debug' and 'Release', but the repository may use other names. +configuration=${configuration:-'Debug'} + +# Set to true to opt out of outputting binary log while running in CI +exclude_ci_binary_log=${exclude_ci_binary_log:-false} + +if [[ "$ci" == true && "$exclude_ci_binary_log" == false ]]; then + binary_log_default=true +else + binary_log_default=false +fi + +# Set to true to output binary log from msbuild. Note that emitting binary log slows down the build. +binary_log=${binary_log:-$binary_log_default} + +# Turns on machine preparation/clean up code that changes the machine state (e.g. kills build processes). +prepare_machine=${prepare_machine:-false} + +# True to restore toolsets and dependencies. +restore=${restore:-true} + +# Adjusts msbuild verbosity level. +verbosity=${verbosity:-'minimal'} + +# Set to true to reuse msbuild nodes. Recommended to not reuse on CI. +if [[ "$ci" == true ]]; then + node_reuse=${node_reuse:-false} +else + node_reuse=${node_reuse:-true} +fi + +# Configures warning treatment in msbuild. +warn_as_error=${warn_as_error:-true} + +# True to attempt using .NET Core already that meets requirements specified in global.json +# installed on the machine instead of downloading one. +use_installed_dotnet_cli=${use_installed_dotnet_cli:-true} + +# Enable repos to use a particular version of the on-line dotnet-install scripts. +# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh +dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'} + +# True to use global NuGet cache instead of restoring packages to repository-local directory. +if [[ "$ci" == true ]]; then + use_global_nuget_cache=${use_global_nuget_cache:-false} +else + use_global_nuget_cache=${use_global_nuget_cache:-true} +fi + +# Used when restoring .NET SDK from alternative feeds +runtime_source_feed=${runtime_source_feed:-''} +runtime_source_feed_key=${runtime_source_feed_key:-''} + +# True if the build is a product build +product_build=${product_build:-false} + +# Resolve any symlinks in the given path. +function ResolvePath { + local path=$1 + + while [[ -h $path ]]; do + local dir="$( cd -P "$( dirname "$path" )" && pwd )" + path="$(readlink "$path")" + + # if $path was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $path != /* ]] && path="$dir/$path" + done + + # return value + _ResolvePath="$path" +} + +# ReadVersionFromJson [json key] +function ReadGlobalVersion { + local key=$1 + + if command -v jq &> /dev/null; then + _ReadGlobalVersion="$(jq -r ".[] | select(has(\"$key\")) | .\"$key\"" "$global_json_file")" + elif [[ "$(cat "$global_json_file")" =~ \"$key\"[[:space:]\:]*\"([^\"]+) ]]; then + _ReadGlobalVersion=${BASH_REMATCH[1]} + fi + + if [[ -z "$_ReadGlobalVersion" ]]; then + Write-PipelineTelemetryError -category 'Build' "Error: Cannot find \"$key\" in $global_json_file" + ExitWithExitCode 1 + fi +} + +function InitializeDotNetCli { + if [[ -n "${_InitializeDotNetCli:-}" ]]; then + return + fi + + local install=$1 + + # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism + export DOTNET_MULTILEVEL_LOOKUP=0 + + # Disable first run since we want to control all package sources + export DOTNET_NOLOGO=1 + + # Disable telemetry on CI + if [[ $ci == true ]]; then + export DOTNET_CLI_TELEMETRY_OPTOUT=1 + fi + + # LTTNG is the logging infrastructure used by Core CLR. Need this variable set + # so it doesn't output warnings to the console. + export LTTNG_HOME="$HOME" + + # Find the first path on $PATH that contains the dotnet.exe + if [[ "$use_installed_dotnet_cli" == true && $global_json_has_runtimes == false && -z "${DOTNET_INSTALL_DIR:-}" ]]; then + local dotnet_path=`command -v dotnet` + if [[ -n "$dotnet_path" ]]; then + ResolvePath "$dotnet_path" + export DOTNET_INSTALL_DIR=`dirname "$_ResolvePath"` + fi + fi + + ReadGlobalVersion "dotnet" + local dotnet_sdk_version=$_ReadGlobalVersion + local dotnet_root="" + + # Use dotnet installation specified in DOTNET_INSTALL_DIR if it contains the required SDK version, + # otherwise install the dotnet CLI and SDK to repo local .dotnet directory to avoid potential permission issues. + if [[ $global_json_has_runtimes == false && -n "${DOTNET_INSTALL_DIR:-}" && -d "$DOTNET_INSTALL_DIR/sdk/$dotnet_sdk_version" ]]; then + dotnet_root="$DOTNET_INSTALL_DIR" + else + dotnet_root="${repo_root}.dotnet" + + export DOTNET_INSTALL_DIR="$dotnet_root" + + if [[ ! -d "$DOTNET_INSTALL_DIR/sdk/$dotnet_sdk_version" ]]; then + if [[ "$install" == true ]]; then + InstallDotNetSdk "$dotnet_root" "$dotnet_sdk_version" + else + Write-PipelineTelemetryError -category 'InitializeToolset' "Unable to find dotnet with SDK version '$dotnet_sdk_version'" + ExitWithExitCode 1 + fi + fi + fi + + # Add dotnet to PATH. This prevents any bare invocation of dotnet in custom + # build steps from using anything other than what we've downloaded. + Write-PipelinePrependPath -path "$dotnet_root" + + Write-PipelineSetVariable -name "DOTNET_MULTILEVEL_LOOKUP" -value "0" + Write-PipelineSetVariable -name "DOTNET_NOLOGO" -value "1" + + # return value + _InitializeDotNetCli="$dotnet_root" +} + +function InstallDotNetSdk { + local root=$1 + local version=$2 + local architecture="unset" + if [[ $# -ge 3 ]]; then + architecture=$3 + fi + InstallDotNet "$root" "$version" $architecture 'sdk' 'true' $runtime_source_feed $runtime_source_feed_key +} + +function InstallDotNet { + local root=$1 + local version=$2 + local runtime=$4 + + local dotnetVersionLabel="'$runtime v$version'" + if [[ -n "${4:-}" ]] && [ "$4" != 'sdk' ]; then + runtimePath="$root" + runtimePath="$runtimePath/shared" + case "$runtime" in + dotnet) + runtimePath="$runtimePath/Microsoft.NETCore.App" + ;; + aspnetcore) + runtimePath="$runtimePath/Microsoft.AspNetCore.App" + ;; + windowsdesktop) + runtimePath="$runtimePath/Microsoft.WindowsDesktop.App" + ;; + *) + ;; + esac + runtimePath="$runtimePath/$version" + + dotnetVersionLabel="runtime toolset '$runtime/$architecture v$version'" + + if [ -d "$runtimePath" ]; then + echo " Runtime toolset '$runtime/$architecture v$version' already installed." + local installSuccess=1 + return + fi + fi + + GetDotNetInstallScript "$root" + local install_script=$_GetDotNetInstallScript + + local installParameters=(--version $version --install-dir "$root") + + if [[ -n "${3:-}" ]] && [ "$3" != 'unset' ]; then + installParameters+=(--architecture $3) + fi + if [[ -n "${4:-}" ]] && [ "$4" != 'sdk' ]; then + installParameters+=(--runtime $4) + fi + if [[ "$#" -ge "5" ]] && [[ "$5" != 'false' ]]; then + installParameters+=(--skip-non-versioned-files) + fi + + local variations=() # list of variable names with parameter arrays in them + + local public_location=("${installParameters[@]}") + variations+=(public_location) + + local dotnetbuilds=("${installParameters[@]}" --azure-feed "https://dotnetbuilds.azureedge.net/public") + variations+=(dotnetbuilds) + + if [[ -n "${6:-}" ]]; then + variations+=(private_feed) + local private_feed=("${installParameters[@]}" --azure-feed $6) + if [[ -n "${7:-}" ]]; then + # The 'base64' binary on alpine uses '-d' and doesn't support '--decode' + # '-d'. To work around this, do a simple detection and switch the parameter + # accordingly. + decodeArg="--decode" + if base64 --help 2>&1 | grep -q "BusyBox"; then + decodeArg="-d" + fi + decodedFeedKey=`echo $7 | base64 $decodeArg` + private_feed+=(--feed-credential $decodedFeedKey) + fi + fi + + local installSuccess=0 + for variationName in "${variations[@]}"; do + local name="$variationName[@]" + local variation=("${!name}") + echo " Attempting to install $dotnetVersionLabel from $variationName." + bash "$install_script" "${variation[@]}" && installSuccess=1 + if [[ "$installSuccess" -eq 1 ]]; then + break + fi + + echo " Failed to install $dotnetVersionLabel from $variationName." + done + + if [[ "$installSuccess" -eq 0 ]]; then + Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to install $dotnetVersionLabel from any of the specified locations." + ExitWithExitCode 1 + fi +} + +function with_retries { + local maxRetries=5 + local retries=1 + echo "Trying to run '$@' for maximum of $maxRetries attempts." + while [[ $((retries++)) -le $maxRetries ]]; do + "$@" + + if [[ $? == 0 ]]; then + echo "Ran '$@' successfully." + return 0 + fi + + timeout=$((3**$retries-1)) + echo "Failed to execute '$@'. Waiting $timeout seconds before next attempt ($retries out of $maxRetries)." 1>&2 + sleep $timeout + done + + echo "Failed to execute '$@' for $maxRetries times." 1>&2 + + return 1 +} + +function GetDotNetInstallScript { + local root=$1 + local install_script="$root/dotnet-install.sh" + local install_script_url="https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh" + + if [[ ! -a "$install_script" ]]; then + mkdir -p "$root" + + echo "Downloading '$install_script_url'" + + # Use curl if available, otherwise use wget + if command -v curl > /dev/null; then + # first, try directly, if this fails we will retry with verbose logging + curl "$install_script_url" -sSL --retry 10 --create-dirs -o "$install_script" || { + if command -v openssl &> /dev/null; then + echo "Curl failed; dumping some information about dotnet.microsoft.com for later investigation" + echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 || true + fi + echo "Will now retry the same URL with verbose logging." + with_retries curl "$install_script_url" -sSL --verbose --retry 10 --create-dirs -o "$install_script" || { + local exit_code=$? + Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to acquire dotnet install script (exit code '$exit_code')." + ExitWithExitCode $exit_code + } + } + else + with_retries wget -v -O "$install_script" "$install_script_url" || { + local exit_code=$? + Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to acquire dotnet install script (exit code '$exit_code')." + ExitWithExitCode $exit_code + } + fi + fi + # return value + _GetDotNetInstallScript="$install_script" +} + +function InitializeBuildTool { + if [[ -n "${_InitializeBuildTool:-}" ]]; then + return + fi + + InitializeDotNetCli $restore + + # return values + _InitializeBuildTool="$_InitializeDotNetCli/dotnet" + _InitializeBuildToolCommand="msbuild" + # use override if it exists - commonly set by source-build + if [[ "${_OverrideArcadeInitializeBuildToolFramework:-x}" == "x" ]]; then + _InitializeBuildToolFramework="net9.0" + else + _InitializeBuildToolFramework="${_OverrideArcadeInitializeBuildToolFramework}" + fi +} + +# Set RestoreNoCache as a workaround for https://github.com/NuGet/Home/issues/3116 +function GetNuGetPackageCachePath { + if [[ -z ${NUGET_PACKAGES:-} ]]; then + if [[ "$use_global_nuget_cache" == true ]]; then + export NUGET_PACKAGES="$HOME/.nuget/packages" + else + export NUGET_PACKAGES="$repo_root/.packages" + export RESTORENOCACHE=true + fi + fi + + # return value + _GetNuGetPackageCachePath=$NUGET_PACKAGES +} + +function InitializeNativeTools() { + if [[ -n "${DisableNativeToolsetInstalls:-}" ]]; then + return + fi + if grep -Fq "native-tools" $global_json_file + then + local nativeArgs="" + if [[ "$ci" == true ]]; then + nativeArgs="--installDirectory $tools_dir" + fi + "$_script_dir/init-tools-native.sh" $nativeArgs + fi +} + +function InitializeToolset { + if [[ -n "${_InitializeToolset:-}" ]]; then + return + fi + + GetNuGetPackageCachePath + + ReadGlobalVersion "Microsoft.DotNet.Arcade.Sdk" + + local toolset_version=$_ReadGlobalVersion + local toolset_location_file="$toolset_dir/$toolset_version.txt" + + if [[ -a "$toolset_location_file" ]]; then + local path=`cat "$toolset_location_file"` + if [[ -a "$path" ]]; then + # return value + _InitializeToolset="$path" + return + fi + fi + + if [[ "$restore" != true ]]; then + Write-PipelineTelemetryError -category 'InitializeToolset' "Toolset version $toolset_version has not been restored." + ExitWithExitCode 2 + fi + + local proj="$toolset_dir/restore.proj" + + local bl="" + if [[ "$binary_log" == true ]]; then + bl="/bl:$log_dir/ToolsetRestore.binlog" + fi + + echo '' > "$proj" + MSBuild-Core "$proj" $bl /t:__WriteToolsetLocation /clp:ErrorsOnly\;NoSummary /p:__ToolsetLocationOutputFile="$toolset_location_file" + + local toolset_build_proj=`cat "$toolset_location_file"` + + if [[ ! -a "$toolset_build_proj" ]]; then + Write-PipelineTelemetryError -category 'Build' "Invalid toolset path: $toolset_build_proj" + ExitWithExitCode 3 + fi + + # return value + _InitializeToolset="$toolset_build_proj" +} + +function ExitWithExitCode { + if [[ "$ci" == true && "$prepare_machine" == true ]]; then + StopProcesses + fi + exit $1 +} + +function StopProcesses { + echo "Killing running build processes..." + pkill -9 "dotnet" || true + pkill -9 "vbcscompiler" || true + return 0 +} + +function MSBuild { + local args=$@ + if [[ "$pipelines_log" == true ]]; then + InitializeBuildTool + InitializeToolset + + if [[ "$ci" == true ]]; then + export NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS=20 + export NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS=20 + Write-PipelineSetVariable -name "NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS" -value "20" + Write-PipelineSetVariable -name "NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS" -value "20" + fi + + local toolset_dir="${_InitializeToolset%/*}" + # new scripts need to work with old packages, so we need to look for the old names/versions + local selectedPath= + local possiblePaths=() + possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.Arcade.Sdk.dll" ) + possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.Arcade.Sdk.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.Arcade.Sdk.dll" ) + for path in "${possiblePaths[@]}"; do + if [[ -f $path ]]; then + selectedPath=$path + break + fi + done + if [[ -z "$selectedPath" ]]; then + Write-PipelineTelemetryError -category 'Build' "Unable to find arcade sdk logger assembly." + ExitWithExitCode 1 + fi + args+=( "-logger:$selectedPath" ) + fi + + MSBuild-Core ${args[@]} +} + +function MSBuild-Core { + if [[ "$ci" == true ]]; then + if [[ "$binary_log" != true && "$exclude_ci_binary_log" != true ]]; then + Write-PipelineTelemetryError -category 'Build' "Binary log must be enabled in CI build, or explicitly opted-out from with the -noBinaryLog switch." + ExitWithExitCode 1 + fi + + if [[ "$node_reuse" == true ]]; then + Write-PipelineTelemetryError -category 'Build' "Node reuse must be disabled in CI build." + ExitWithExitCode 1 + fi + fi + + InitializeBuildTool + + local warnaserror_switch="" + if [[ $warn_as_error == true ]]; then + warnaserror_switch="/warnaserror" + fi + + function RunBuildTool { + export ARCADE_BUILD_TOOL_COMMAND="$_InitializeBuildTool $@" + + "$_InitializeBuildTool" "$@" || { + local exit_code=$? + # We should not Write-PipelineTaskError here because that message shows up in the build summary + # The build already logged an error, that's the reason it failed. Producing an error here only adds noise. + echo "Build failed with exit code $exit_code. Check errors above." + + # When running on Azure Pipelines, override the returned exit code to avoid double logging. + # Skip this when the build is a child of the VMR orchestrator build. + if [[ "$ci" == true && -n ${SYSTEM_TEAMPROJECT:-} && "$product_build" != true && "$properties" != *"DotNetBuildRepo=true"* ]]; then + Write-PipelineSetResult -result "Failed" -message "msbuild execution failed." + # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error + # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error + ExitWithExitCode 0 + else + ExitWithExitCode $exit_code + fi + } + } + + RunBuildTool "$_InitializeBuildToolCommand" /m /nologo /clp:Summary /v:$verbosity /nr:$node_reuse $warnaserror_switch /p:TreatWarningsAsErrors=$warn_as_error /p:ContinuousIntegrationBuild=$ci "$@" +} + +function GetDarc { + darc_path="$temp_dir/darc" + version="$1" + + if [[ -n "$version" ]]; then + version="--darcversion $version" + fi + + "$eng_root/common/darc-init.sh" --toolpath "$darc_path" $version +} + +ResolvePath "${BASH_SOURCE[0]}" +_script_dir=`dirname "$_ResolvePath"` + +. "$_script_dir/pipeline-logging-functions.sh" + +eng_root=`cd -P "$_script_dir/.." && pwd` +repo_root=`cd -P "$_script_dir/../.." && pwd` +repo_root="${repo_root}/" +artifacts_dir="${repo_root}artifacts" +toolset_dir="$artifacts_dir/toolset" +tools_dir="${repo_root}.tools" +log_dir="$artifacts_dir/log/$configuration" +temp_dir="$artifacts_dir/tmp/$configuration" + +global_json_file="${repo_root}global.json" +# determine if global.json contains a "runtimes" entry +global_json_has_runtimes=false +if command -v jq &> /dev/null; then + if jq -e '.tools | has("runtimes")' "$global_json_file" &> /dev/null; then + global_json_has_runtimes=true + fi +elif [[ "$(cat "$global_json_file")" =~ \"runtimes\"[[:space:]\:]*\{ ]]; then + global_json_has_runtimes=true +fi + +# HOME may not be defined in some scenarios, but it is required by NuGet +if [[ -z $HOME ]]; then + export HOME="${repo_root}artifacts/.home/" + mkdir -p "$HOME" +fi + +mkdir -p "$toolset_dir" +mkdir -p "$temp_dir" +mkdir -p "$log_dir" + +Write-PipelineSetVariable -name "Artifacts" -value "$artifacts_dir" +Write-PipelineSetVariable -name "Artifacts.Toolset" -value "$toolset_dir" +Write-PipelineSetVariable -name "Artifacts.Log" -value "$log_dir" +Write-PipelineSetVariable -name "Temp" -value "$temp_dir" +Write-PipelineSetVariable -name "TMP" -value "$temp_dir" + +# Import custom tools configuration, if present in the repo. +if [ -z "${disable_configure_toolset_import:-}" ]; then + configure_toolset_script="$eng_root/configure-toolset.sh" + if [[ -a "$configure_toolset_script" ]]; then + . "$configure_toolset_script" + fi +fi + +# TODO: https://github.com/dotnet/arcade/issues/1468 +# Temporary workaround to avoid breaking change. +# Remove once repos are updated. +if [[ -n "${useInstalledDotNetCli:-}" ]]; then + use_installed_dotnet_cli="$useInstalledDotNetCli" +fi diff --git a/eng/configure-toolset.ps1 b/eng/configure-toolset.ps1 new file mode 100644 index 000000000..931654d41 --- /dev/null +++ b/eng/configure-toolset.ps1 @@ -0,0 +1,3 @@ +# SdkTests do not currently work with globally installed CLI as they use dotnet-install.ps1 to install more runtimes + +$script:useInstalledDotNetCli = $false \ No newline at end of file diff --git a/eng/configure-toolset.sh b/eng/configure-toolset.sh new file mode 100644 index 000000000..1e0b58a36 --- /dev/null +++ b/eng/configure-toolset.sh @@ -0,0 +1,6 @@ +# SdkTests do not currently work with globally installed CLI as they use dotnet-install.ps1 to install more runtimes + +useInstalledDotNetCli="false" + +# Working around issue https://github.com/dotnet/arcade/issues/7327 +DisableNativeToolsetInstalls=true \ No newline at end of file diff --git a/eng/core-sdk-build-env.sh b/eng/core-sdk-build-env.sh new file mode 100644 index 000000000..522887453 --- /dev/null +++ b/eng/core-sdk-build-env.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done + +REPO_ROOT="$( cd -P "$( dirname "$SOURCE" )/../" && pwd )" + +arcade_partition= + +while [[ $# > 0 ]]; do + opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')" + case "$opt" in + -partition) + arcade_partition=$2 + shift + ;; + esac + + shift +done + +export ARCADE_PARTITION=$arcade_partition + +if [[ ! -z "$arcade_partition" ]]; then + arcade_partition_suffix="-$arcade_partition" +fi + +export PATH=$REPO_ROOT/.dotnet$arcade_partition_suffix:$PATH +export DOTNET_INSTALL_DIR=$REPO_ROOT/.dotnet$arcade_partition_suffix +export ArtifactsDir=$REPO_ROOT/artifacts$arcade_partition_suffix/ + +export DOTNET_MULTILEVEL_LOOKUP=0 \ No newline at end of file diff --git a/eng/core-sdk-test-env.bat b/eng/core-sdk-test-env.bat new file mode 100644 index 000000000..37a7c0942 --- /dev/null +++ b/eng/core-sdk-test-env.bat @@ -0,0 +1,15 @@ +@echo off +REM Copyright (c) .NET Foundation and contributors. All rights reserved. +REM Licensed under the MIT license. See LICENSE file in the project root for full license information. + +REM Get normalized version of parent path +for %%i in (%~dp0..\) DO ( + SET CLI_REPO_ROOT=%%~dpi +) + +title Core SDK Test (%CLI_REPO_ROOT%) + +REM Add Stage 2 CLI to path +set PATH=%CLI_REPO_ROOT%artifacts\bin\redist\Debug\dotnet;%PATH% + +set DOTNET_MULTILEVEL_LOOKUP=0 diff --git a/eng/core-sdk-test-env.sh b/eng/core-sdk-test-env.sh new file mode 100644 index 000000000..e99e3729c --- /dev/null +++ b/eng/core-sdk-test-env.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done + +REPO_ROOT="$( cd -P "$( dirname "$SOURCE" )/../" && pwd )" + +STAGE2_DIR=$REPO_ROOT/artifacts/bin/redist/Debug/dotnet + +export PATH=$STAGE2_DIR:$PATH + +export DOTNET_MULTILEVEL_LOOKUP=0 \ No newline at end of file diff --git a/eng/loc/wxl_loc.lss b/eng/loc/wxl_loc.lss new file mode 100644 index 000000000..2de8dd07f --- /dev/null +++ b/eng/loc/wxl_loc.lss @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/localization.yml b/eng/localization.yml new file mode 100644 index 000000000..aa253c85c --- /dev/null +++ b/eng/localization.yml @@ -0,0 +1,28 @@ +# Pipeline: https://dnceng.visualstudio.com/internal/_build?definitionId=1224 + +schedules: +# Cron timezone is UTC. +- cron: "0 18 * * *" + displayName: Run tests daily at 6 PM UTC (11 AM PT) + branches: + include: + - main + always: true + +# Do not run in PR builds nor support other triggers. +pr: none +trigger: none + +variables: +- name: _TeamName + value: SDK + +jobs: +- ${{ if and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), eq(variables['Build.Reason'], 'Manual'))) }}: + - template: /eng/common/templates/job/onelocbuild.yml + parameters: + CreatePr: ${{ ne(variables['Build.Reason'], 'Manual') }} + LclPackageId: 'LCL-JUNO-PROD-DOTNETINSTALLER' + LclSource: lclFilesFromPackage + MirrorBranch: main + MirrorRepo: installer \ No newline at end of file diff --git a/eng/restore-toolset.ps1 b/eng/restore-toolset.ps1 new file mode 100644 index 000000000..ff7534782 --- /dev/null +++ b/eng/restore-toolset.ps1 @@ -0,0 +1,47 @@ +function InitializeCustomSDKToolset { + if (-not $restore) { + return + } + + # InstallDotNetSharedFramework "1.0.5" + # InstallDotNetSharedFramework "1.1.2" + # InstallDotNetSharedFramework "2.1.0" + + CreateBuildEnvScript +} + +function CreateBuildEnvScript() +{ + InitializeBuildTool | Out-Null # Make sure DOTNET_INSTALL_DIR is set + + Create-Directory $ArtifactsDir + $scriptPath = Join-Path $ArtifactsDir "core-sdk-build-env.bat" + $scriptContents = @" +@echo off +title Core SDK Build ($RepoRoot) +set DOTNET_MULTILEVEL_LOOKUP=0 + +set PATH=$env:DOTNET_INSTALL_DIR;%PATH% +set NUGET_PACKAGES=$env:NUGET_PACKAGES + +set DOTNET_ROOT=$env:DOTNET_INSTALL_DIR +"@ + + Out-File -FilePath $scriptPath -InputObject $scriptContents -Encoding ASCII +} + +function InstallDotNetSharedFramework([string]$version) { + $dotnetRoot = $env:DOTNET_INSTALL_DIR + $fxDir = Join-Path $dotnetRoot "shared\Microsoft.NETCore.App\$version" + + if (!(Test-Path $fxDir)) { + $installScript = GetDotNetInstallScript $dotnetRoot + & $installScript -Version $version -InstallDir $dotnetRoot -Runtime "dotnet" + + if($lastExitCode -ne 0) { + throw "Failed to install shared Framework $version to '$dotnetRoot' (exit code '$lastExitCode')." + } + } +} + +InitializeCustomSDKToolset \ No newline at end of file diff --git a/eng/restore-toolset.sh b/eng/restore-toolset.sh new file mode 100644 index 000000000..6305387d5 --- /dev/null +++ b/eng/restore-toolset.sh @@ -0,0 +1,26 @@ +function InitializeCustomSDKToolset { + if [[ "$restore" != true ]]; then + return + fi + + #InstallDotNetSharedFramework "1.0.5" + #InstallDotNetSharedFramework "1.1.2" + + InitializeDotNetCli true +} + +# Installs additional shared frameworks for testing purposes +function InstallDotNetSharedFramework { + local version=$1 + local dotnet_root=$DOTNET_INSTALL_DIR + local fx_dir="$dotnet_root/shared/Microsoft.NETCore.App/$version" + + if [[ ! -d "$fx_dir" ]]; then + GetDotNetInstallScript "$dotnet_root" + local install_script=$_GetDotNetInstallScript + + bash "$install_script" --version $version --install-dir "$dotnet_root" --runtime "dotnet" || true + fi +} + +InitializeCustomSDKToolset \ No newline at end of file diff --git a/eng/sdl-tsa-vars.config b/eng/sdl-tsa-vars.config new file mode 100644 index 000000000..433230ad5 --- /dev/null +++ b/eng/sdl-tsa-vars.config @@ -0,0 +1,10 @@ +-SourceToolsList @("policheck","credscan") +-TsaInstanceURL https://devdiv.visualstudio.com/ +-TsaProjectName DEVDIV +-TsaNotificationEmail dotnetdevexcli@microsoft.com +-TsaCodebaseAdmin REDMOND\marcpop +-TsaBugAreaPath "DevDiv\NET Tools\SDK" +-TsaIterationPath DevDiv +-TsaRepositoryName dotnet-installer +-TsaCodebaseName dotnet-installer +-TsaPublish $True diff --git a/global.json b/global.json new file mode 100644 index 000000000..5f70a2eb7 --- /dev/null +++ b/global.json @@ -0,0 +1,18 @@ +{ + "tools": { + "dotnet": "9.0.100-preview.5.24225.4", + "runtimes": { + "dotnet": [ + "$(VSRedistCommonNetCoreSharedFrameworkx6490PackageVersion)" + ] + } + }, + "native-tools": { + "cmake": "latest" + }, + "msbuild-sdks": { + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24270.3", + "Microsoft.DotNet.CMake.Sdk": "9.0.0-beta.24270.3", + "Microsoft.Build.NoTargets": "3.7.0" + } +} diff --git a/resources/images/version_badge.svg b/resources/images/version_badge.svg new file mode 100644 index 000000000..4e4577974 --- /dev/null +++ b/resources/images/version_badge.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + version + version + ver_number + ver_number + + \ No newline at end of file diff --git a/run-build.ps1 b/run-build.ps1 new file mode 100644 index 000000000..226dd8ade --- /dev/null +++ b/run-build.ps1 @@ -0,0 +1,42 @@ +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +[CmdletBinding(PositionalBinding=$false)] +param( + [string]$Configuration="Debug", + [string]$Architecture="x64", + [switch]$Sign=$false, + [switch]$PgoInstrument, + [bool]$WarnAsError=$true, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$ExtraParameters +) + +$RepoRoot = "$PSScriptRoot" + +$Parameters = "/p:Architecture=$Architecture" +$Parameters = "$Parameters -configuration $Configuration" + +if ($PgoInstrument) { + $Parameters = "$Parameters /p:PgoInstrument=true" +} + +if ($Sign) { + $Parameters = "$Parameters -sign /p:SignCoreSdk=true" +} + +$Parameters = "$Parameters -WarnAsError `$$WarnAsError" + +try { + $ExpressionToInvoke = "$RepoRoot\eng\common\build.ps1 -restore -build -nativeToolsOnMachine $Parameters $ExtraParameters" + Write-Host "Invoking expression: $ExpressionToInvoke" + Invoke-Expression $ExpressionToInvoke +} +catch { + Write-Error $_ + Write-Error $_.ScriptStackTrace + throw "Failed to build" +} + +if($LASTEXITCODE -ne 0) { throw "Failed to build" } diff --git a/run-build.sh b/run-build.sh new file mode 100755 index 000000000..c380d75ce --- /dev/null +++ b/run-build.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +set -e + +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done +DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" +REPOROOT="$DIR" + +ARCHITECTURE="x64" + +source "$REPOROOT/eng/_prettyprint.sh" + +LINUX_PORTABLE_INSTALL_ARGS= +CUSTOM_BUILD_ARGS= + +# Set nuget package cache under the repo +[ -z $NUGET_PACKAGES ] && export NUGET_PACKAGES="$REPOROOT/.nuget/packages" + +args=( ) + +while [[ $# > 0 ]]; do + lowerI="$(echo $1 | awk '{print tolower($0)}')" + case $lowerI in + -a|--architecture) + ARCHITECTURE="$2" + args+=("/p:Architecture=$ARCHITECTURE") + shift + ;; + --runtime-id) + args+=("/p:Rid=\"$2\"") + shift + ;; + --linux-portable) + args+=("/p:Rid=linux-x64 /p:OSName=\"linux\" /p:IslinuxPortable=\"true\"") + ;; + --pgoInstrument) + args+=("/p:PgoInstrument=true") + ;; + --help) + echo "Usage: $0 [--configuration ] [--architecture ] [--docker ] [--help]" + echo "" + echo "Options:" + echo " --configuration Build the specified Configuration (Debug or Release, default: Debug)" + echo " --architecture Build the specified architecture (x64, arm or arm64 , default: x64)" + echo " --docker Build in Docker using the Dockerfile located in scripts/docker/IMAGENAME" + echo " --help Display this help message" + exit 0 + ;; + *) + args+=("$1") + ;; + esac + + shift +done + +source $REPOROOT/eng/common/native/init-os-and-arch.sh +source $REPOROOT/eng/common/native/init-distro-rid.sh +initDistroRidGlobal "$os" "$arch" "" + +. "$REPOROOT/eng/common/build.sh" --build --restore "${args[@]}" diff --git a/src/SourceBuild/patches/runtime/0002-Update-MSBuild-dependencies.patch b/src/SourceBuild/patches/runtime/0002-Update-MSBuild-dependencies.patch new file mode 100644 index 000000000..267e45546 --- /dev/null +++ b/src/SourceBuild/patches/runtime/0002-Update-MSBuild-dependencies.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nikola Milosavljevic +Date: Tue, 23 Apr 2024 01:55:17 +0000 +Subject: [PATCH] Update MSBuild dependencies + +Backport: https://github.com/dotnet/runtime/issues/101395 +--- + eng/Version.Details.xml | 12 ++++++++++++ + eng/Versions.props | 6 +++--- + src/tasks/AotCompilerTask/MonoAOTCompiler.csproj | 1 - + src/tasks/WasmAppBuilder/WasmAppBuilder.csproj | 3 --- + 4 files changed, 15 insertions(+), 7 deletions(-) + +diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml +index fe35dc0997e..b90337395f6 100644 +--- a/eng/Version.Details.xml ++++ b/eng/Version.Details.xml +@@ -414,6 +414,18 @@ + https://github.com/dotnet/msbuild + 195e7f5a3a8e51c37d83cd9e54cb99dc3fc69c22 + ++ ++ https://github.com/dotnet/msbuild ++ 195e7f5a3a8e51c37d83cd9e54cb99dc3fc69c22 ++ ++ ++ https://github.com/dotnet/msbuild ++ 195e7f5a3a8e51c37d83cd9e54cb99dc3fc69c22 ++ ++ ++ https://github.com/dotnet/msbuild ++ 195e7f5a3a8e51c37d83cd9e54cb99dc3fc69c22 ++ + + https://github.com/dotnet/msbuild + 195e7f5a3a8e51c37d83cd9e54cb99dc3fc69c22 +diff --git a/eng/Versions.props b/eng/Versions.props +index f012e409095..bf16c6ee71f 100644 +--- a/eng/Versions.props ++++ b/eng/Versions.props +@@ -173,9 +173,9 @@ + 1.0.4-preview6.19326.1 + 2.0.5 + 17.8.3 +- $(MicrosoftBuildVersion) +- $(MicrosoftBuildVersion) +- $(MicrosoftBuildVersion) ++ 17.8.3 ++ 17.8.3 ++ 17.8.3 + 6.2.4 + 6.2.4 + 7.0.412701 +diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj +index e76730b5aec..88ae0fb136c 100644 +--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj ++++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj +@@ -14,7 +14,6 @@ + + + +- + + + +diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +index 34a51095986..d8c95954dd8 100644 +--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj ++++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +@@ -24,9 +24,6 @@ + + + +- +- +- + + + diff --git a/src/core-sdk-tasks/AddMetadataIsPE.cs b/src/core-sdk-tasks/AddMetadataIsPE.cs new file mode 100644 index 000000000..b0e894741 --- /dev/null +++ b/src/core-sdk-tasks/AddMetadataIsPE.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.IO; +using System.Reflection.PortableExecutable; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Cli.Build +{ + public class AddMetadataIsPE : Task + { + [Required] + public ITaskItem[] Items { get; set; } + + [Output] + public ITaskItem[] ResultItems { get; set; } + + public override bool Execute() + { + var resultItemsList = new List(); + + foreach (var item in Items) + { + var resultItem = new TaskItem(item); + item.CopyMetadataTo(resultItem); + + var isPe = File.Exists(resultItem.GetMetadata("FullPath")) && HasMetadata(resultItem.GetMetadata("FullPath")); + resultItem.SetMetadata("IsPE", isPe.ToString()); + + resultItemsList.Add(resultItem); + } + + ResultItems = resultItemsList.ToArray(); + + return true; + } + + private static bool HasMetadata(string pathToFile) + { + try + { + using (var inStream = File.OpenRead(pathToFile)) + { + using (var peReader = new PEReader(inStream)) + { + return peReader.HasMetadata; + } + } + } + catch (BadImageFormatException) { } + + return false; + } + } +} diff --git a/src/core-sdk-tasks/AzurePublisher.cs b/src/core-sdk-tasks/AzurePublisher.cs new file mode 100644 index 000000000..bd25f079e --- /dev/null +++ b/src/core-sdk-tasks/AzurePublisher.cs @@ -0,0 +1,196 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !SOURCE_BUILD +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Auth; +using Microsoft.WindowsAzure.Storage.Blob; + +namespace Microsoft.DotNet.Cli.Build +{ + public class AzurePublisher + { + public enum Product + { + SharedFramework, + Host, + HostFxr, + Sdk, + } + + private const string s_dotnetBlobContainerName = "dotnet"; + + private string _containerName { get; set; } + private CloudBlobContainer _blobContainer { get; set; } + + public AzurePublisher(string accountName, string accountKey, string containerName = s_dotnetBlobContainerName) + { + _containerName = containerName; + _blobContainer = GetDotnetBlobContainer(accountName, accountKey, containerName); + } + + private CloudBlobContainer GetDotnetBlobContainer(string accountName, string accountKey, string containerName) + { + var storageCredentials = new StorageCredentials(accountName, accountKey); + var storageAccount = new CloudStorageAccount(storageCredentials, true); + return storageAccount.CreateCloudBlobClient().GetContainerReference(containerName); + } + + public string UploadFile(string file, Product product, string version) + { + string url = CalculateRelativePathForFile(file, product, version); + CloudBlockBlob blob = _blobContainer.GetBlockBlobReference(url); + blob.UploadFromFileAsync(file).Wait(); + SetBlobPropertiesBasedOnFileType(blob); + return url; + } + + public void PublishStringToBlob(string blob, string content) + { + CloudBlockBlob blockBlob = _blobContainer.GetBlockBlobReference(blob); + blockBlob.UploadTextAsync(content).Wait(); + + SetBlobPropertiesBasedOnFileType(blockBlob); + } + + public void CopyBlob(string sourceBlob, string targetBlob) + { + CloudBlockBlob source = _blobContainer.GetBlockBlobReference(sourceBlob); + CloudBlockBlob target = _blobContainer.GetBlockBlobReference(targetBlob); + + // Create the empty blob + using (MemoryStream ms = new MemoryStream()) + { + target.UploadFromStreamAsync(ms).Wait(); + } + + // Copy actual blob data + target.StartCopyAsync(source).Wait(); + } + + public void SetBlobPropertiesBasedOnFileType(string path) + { + CloudBlockBlob blob = _blobContainer.GetBlockBlobReference(path); + SetBlobPropertiesBasedOnFileType(blob); + } + + private void SetBlobPropertiesBasedOnFileType(CloudBlockBlob blockBlob) + { + if (Path.GetExtension(blockBlob.Uri.AbsolutePath.ToLower()) == ".svg") + { + blockBlob.Properties.ContentType = "image/svg+xml"; + blockBlob.Properties.CacheControl = "no-cache"; + blockBlob.SetPropertiesAsync().Wait(); + } + else if (Path.GetExtension(blockBlob.Uri.AbsolutePath.ToLower()) == ".version") + { + blockBlob.Properties.ContentType = "text/plain"; + blockBlob.Properties.CacheControl = "no-cache"; + blockBlob.SetPropertiesAsync().Wait(); + } + } + + public IEnumerable ListBlobs(Product product, string version) => ListBlobs($"{product}/{version}"); + + public IEnumerable ListBlobs(string virtualDirectory) + { + CloudBlobDirectory blobDir = _blobContainer.GetDirectoryReference(virtualDirectory); + BlobContinuationToken continuationToken = new BlobContinuationToken(); + + var blobFiles = blobDir.ListBlobsSegmentedAsync(continuationToken).Result; + return blobFiles.Results.Select(bf => bf.Uri.PathAndQuery.Replace($"/{_containerName}/", string.Empty)); + } + + public string AcquireLeaseOnBlob(string blob, TimeSpan? maxWaitDefault = null, TimeSpan? delayDefault = null) + { + TimeSpan maxWait = maxWaitDefault ?? TimeSpan.FromSeconds(120); + TimeSpan delay = delayDefault ?? TimeSpan.FromMilliseconds(500); + + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + + // This will throw an exception with HTTP code 409 when we cannot acquire the lease + // But we should block until we can get this lease, with a timeout (maxWaitSeconds) + while (stopWatch.ElapsedMilliseconds < maxWait.TotalMilliseconds) + { + try + { + CloudBlockBlob cloudBlob = _blobContainer.GetBlockBlobReference(blob); + Task task = cloudBlob.AcquireLeaseAsync(TimeSpan.FromMinutes(1), null); + task.Wait(); + return task.Result; + } + catch (Exception e) + { + Console.WriteLine($"Retrying lease acquisition on {blob}, {e.Message}"); + Thread.Sleep(delay); + } + } + + throw new Exception($"Unable to acquire lease on {blob}"); + } + + public void ReleaseLeaseOnBlob(string blob, string leaseId) + { + CloudBlockBlob cloudBlob = _blobContainer.GetBlockBlobReference(blob); + AccessCondition ac = new AccessCondition() { LeaseId = leaseId }; + cloudBlob.ReleaseLeaseAsync(ac).Wait(); + } + + public bool IsLatestSpecifiedVersion(string version) + { + Task task = _blobContainer.GetBlockBlobReference(version).ExistsAsync(); + task.Wait(); + return task.Result; + } + + public void DropLatestSpecifiedVersion(string version) + { + CloudBlockBlob blob = _blobContainer.GetBlockBlobReference(version); + using (MemoryStream ms = new MemoryStream()) + { + blob.UploadFromStreamAsync(ms).Wait(); + } + } + + public void CreateBlobIfNotExists(string path) + { + Task task = _blobContainer.GetBlockBlobReference(path).ExistsAsync(); + task.Wait(); + if (!task.Result) + { + CloudBlockBlob blob = _blobContainer.GetBlockBlobReference(path); + using (MemoryStream ms = new MemoryStream()) + { + blob.UploadFromStreamAsync(ms).Wait(); + } + } + } + + public bool TryDeleteBlob(string path) + { + try + { + DeleteBlob(path); + return true; + } + catch (Exception e) + { + Console.WriteLine($"Deleting blob {path} failed with \r\n{e.Message}"); + return false; + } + } + + private void DeleteBlob(string path) => _blobContainer.GetBlockBlobReference(path).DeleteAsync().Wait(); + + private static string CalculateRelativePathForFile(string file, Product product, string version) => $"{product}/{version}/{Path.GetFileName(file)}"; + } +} +#endif diff --git a/src/core-sdk-tasks/BuildFPMToolPreReqs.cs b/src/core-sdk-tasks/BuildFPMToolPreReqs.cs new file mode 100644 index 000000000..b19bf217d --- /dev/null +++ b/src/core-sdk-tasks/BuildFPMToolPreReqs.cs @@ -0,0 +1,303 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; +using System.IO; +using System.Text; +using Newtonsoft.Json; + +namespace Microsoft.DotNet.Build.Tasks +{ + /// + /// This task prepares the command line parameters for running a RPM build using FPM tool and also updates the copyright and changelog file tokens. + /// If parses various values from the config json by first reading it into a model and then builds the required string for parameters and passes it back. + /// + /// + public class BuildFPMToolPreReqs : Task + { + [Required] + public string InputDir { get; set; } + + [Required] + public string OutputDir { get; set; } + + [Required] + public string PackageVersion { get; set; } + + [Required] + public string ConfigJsonFile { get; set; } + + [Output] + public string FPMParameters { get; set; } + + public override bool Execute() + { + if (!File.Exists(ConfigJsonFile)) + { + throw new FileNotFoundException($"Expected file {ConfigJsonFile} was not found."); + } + + // Open the Config Json and read the values into the model + TextReader projectFileReader = File.OpenText(ConfigJsonFile); + string jsonFileText = projectFileReader.ReadToEnd(); + ConfigJson configJson = JsonConvert.DeserializeObject(jsonFileText); + + // Update the Changelog and Copyright files by replacing tokens with values from config json + UpdateChangelog(configJson, PackageVersion); + UpdateCopyright(configJson); + + // Build the full list of parameters + FPMParameters = BuildCmdParameters(configJson, PackageVersion); + Log.LogMessage(MessageImportance.Normal, "Generated RPM parameters: " + FPMParameters); + + return !Log.HasLoggedErrors; + } + + // Update the tokens in the changelog file from the config Json + private void UpdateChangelog(ConfigJson configJson, string package_version) + { + string changelogFile = Path.Combine(InputDir, "templates", "changelog"); + if (!File.Exists(changelogFile)) + { + throw new FileNotFoundException($"Expected file {changelogFile} was not found."); + } + string str = File.ReadAllText(changelogFile); + str = str.Replace("{PACKAGE_NAME}", configJson.Package_Name); + str = str.Replace("{PACKAGE_VERSION}", package_version); + str = str.Replace("{PACKAGE_REVISION}", configJson.Release.Package_Revision); + str = str.Replace("{CHANGELOG_MESSAGE}", configJson.Release.Changelog_Message); + str = str.Replace("{MAINTAINER_NAME}", configJson.Maintainer_Name); + str = str.Replace("{MAINTAINER_EMAIL}", configJson.Maintainer_Email); + // The date format needs to be like Wed May 17 2017 + str = str.Replace("{DATE}", DateTime.UtcNow.ToString("ddd MMM dd yyyy")); + File.WriteAllText(changelogFile, str); + } + + public void UpdateCopyright(ConfigJson configJson) + { + string copyrightFile = Path.Combine(InputDir, "templates", "copyright"); + if (!File.Exists(copyrightFile)) + { + throw new FileNotFoundException($"Expected file {copyrightFile} was not found."); + } + string str = File.ReadAllText(copyrightFile); + str = str.Replace("{COPYRIGHT_TEXT}", configJson.CopyRight); + str = str.Replace("{LICENSE_NAME}", configJson.License.Type); + str = str.Replace("{LICENSE_TEXT}", configJson.License.Full_Text); + File.WriteAllText(copyrightFile, str); + } + + private string BuildCmdParameters(ConfigJson configJson, string package_version) + { + // Parameter list that needs to be passed to FPM tool: + // -s : is the input source type(dir) --Static + // -t : is the type of package(rpm) --Static + // -n : is for the name of the package --JSON + // -v : is the version to give to the package --ARG + // -a : architecture --JSON + // -d : is for all dependent packages. This can be used multiple times to specify the dependencies of the package. --JSON + // --rpm-os : the operating system to target this rpm --Static + // --rpm-digest : rpm digest algorithm --Static + // --rpm-changelog : the changelog from FILEPATH contents --ARG + // --rpm-summary : it is the RPM summary that shows in the Title --JSON + // --description : it is the description for the package --JSON + // -p : The actual package name (with path) for your package. --ARG+JSON + // --conflicts : Other packages/versions this package conflicts with provided as CSV --JSON + // --directories : Recursively add directories as being owned by the package. --JSON + // --after-install : FILEPATH to the script to be run after install of the package --JSON + // --after-remove : FILEPATH to the script to be run after package removal --JSON + // --license : the licensing name for the package. This will include the license type in the meta-data for the package, but will not include the associated license file within the package itself. --JSON + // --iteration : the iteration to give to the package. This comes from the package_revision --JSON + // --url : url for this package. --JSON + // --verbose : Set verbose output for FPM tool --Static + // : Add all the folder mappings for package_root, docs, man pages --Static + + var parameters = new List + { + "-s dir", + "-t rpm", + string.Concat("-n ", configJson.Package_Name), + string.Concat("-v ", package_version), + string.Concat("-a ", configJson.Control.Architecture) + }; + + // Build the list of dependencies as -d -d + if (configJson.Rpm_Dependencies != null) + { + foreach (RpmDependency rpmdep in configJson.Rpm_Dependencies) + { + if (rpmdep.Package_Name != "") + { + // If no version is specified then the dependency is just the package without >= check + string dependency = rpmdep.Package_Version != "" ? + string.Concat(rpmdep.Package_Name, " >= ", rpmdep.Package_Version) : + rpmdep.Package_Name; + + parameters.Add(string.Concat("-d ", EscapeArg(dependency))); + } + } + } + + // Build the list of owned directories + if (configJson.Directories != null) + { + foreach (string dir in configJson.Directories) + { + if (dir != "") + { + parameters.Add(string.Concat("--directories ", EscapeArg(dir))); + } + } + } + + parameters.Add("--rpm-os linux"); + parameters.Add("--rpm-digest sha256"); + parameters.Add(string.Concat("--rpm-changelog ", + EscapeArg(Path.Combine(InputDir, "templates", "changelog")))); // Changelog File + parameters.Add(string.Concat("--rpm-summary ", EscapeArg(configJson.Short_Description))); + parameters.Add(string.Concat("--description ", EscapeArg(configJson.Long_Description))); + parameters.Add(string.Concat("--maintainer ", + EscapeArg(configJson.Maintainer_Name + " <" + configJson.Maintainer_Email + ">"))); + parameters.Add(string.Concat("--vendor ", EscapeArg(configJson.Vendor))); + parameters.Add(string.Concat("-p ", Path.Combine(OutputDir, configJson.Package_Name + ".rpm"))); + if (configJson.Package_Conflicts != null) + parameters.Add(string.Concat("--conflicts ", + EscapeArg(string.Join(",", configJson.Package_Conflicts)))); + if (configJson.After_Install_Source != null) + parameters.Add(string.Concat("--after-install ", + Path.Combine(InputDir, EscapeArg(configJson.After_Install_Source)))); + if (configJson.After_Remove_Source != null) + parameters.Add(string.Concat("--after-remove ", + Path.Combine(InputDir, EscapeArg(configJson.After_Remove_Source)))); + parameters.Add(string.Concat("--license ", EscapeArg(configJson.License.Type))); + parameters.Add(string.Concat("--iteration ", configJson.Release.Package_Revision)); + parameters.Add(string.Concat("--url ", "\"", EscapeArg(configJson.Homepage), "\"")); + parameters.Add("--verbose"); + + // Map all the payload directories as they need to install on the system + if (configJson.Install_Root != null) + parameters.Add(string.Concat(Path.Combine(InputDir, "package_root/="), configJson.Install_Root)); // Package Files + if (configJson.Install_Man != null) + parameters.Add(string.Concat(Path.Combine(InputDir, "docs", "host/="), configJson.Install_Man)); // Man Pages + if (configJson.Install_Doc != null) + parameters.Add(string.Concat(Path.Combine(InputDir, "templates", "copyright="), configJson.Install_Doc)); // CopyRight File + + return string.Join(" ", parameters); + } + + private string EscapeArg(string arg) + { + var sb = new StringBuilder(); + + bool quoted = ShouldSurroundWithQuotes(arg); + if (quoted) sb.Append("\""); + + for (int i = 0; i < arg.Length; ++i) + { + var backslashCount = 0; + + // Consume All Backslashes + while (i < arg.Length && arg[i] == '\\') + { + backslashCount++; + i++; + } + + // Escape any backslashes at the end of the arg + // This ensures the outside quote is interpreted as + // an argument delimiter + if (i == arg.Length) + { + sb.Append('\\', 2 * backslashCount); + } + // Escape any preceding backslashes and the quote + else if (arg[i] == '"') + { + sb.Append('\\', (2 * backslashCount) + 1); + sb.Append('"'); + } + // Output any consumed backslashes and the character + else + { + sb.Append('\\', backslashCount); + sb.Append(arg[i]); + } + } + + if (quoted) sb.Append("\""); + + return sb.ToString(); + } + + private bool ShouldSurroundWithQuotes(string argument) + { + // Don't quote already quoted strings + if (argument.StartsWith("\"", StringComparison.Ordinal) && + argument.EndsWith("\"", StringComparison.Ordinal)) + { + return false; + } + + // Only quote if whitespace exists in the string + if (argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n")) + { + return true; + } + + return false; + } + } + + /// + /// Model classes for reading and storing the JSON. + /// + public class ConfigJson + { + public string Maintainer_Name { get; set; } + public string Maintainer_Email { get; set; } + public string Vendor { get; set; } + public string Package_Name { get; set; } + public string Install_Root { get; set; } + public string Install_Doc { get; set; } + public string Install_Man { get; set; } + public string Short_Description { get; set; } + public string Long_Description { get; set; } + public string Homepage { get; set; } + public string CopyRight { get; set; } + public Release Release { get; set; } + public Control Control { get; set; } + public License License { get; set; } + public List Rpm_Dependencies { get; set; } + public List Package_Conflicts { get; set; } + public List Directories { get; set; } + public string After_Install_Source { get; set; } + public string After_Remove_Source { get; set; } + } + + public class Release + { + public string Package_Version { get; set; } + public string Package_Revision { get; set; } + public string Changelog_Message { get; set; } + } + + public class Control + { + public string Architecture { get; set; } + } + + public class License + { + public string Type { get; set; } + public string Full_Text { get; set; } + } + + public class RpmDependency + { + public string Package_Name { get; set; } + public string Package_Version { get; set; } + } +} \ No newline at end of file diff --git a/src/core-sdk-tasks/CalculateTemplateVersions.cs b/src/core-sdk-tasks/CalculateTemplateVersions.cs new file mode 100644 index 000000000..0774a3858 --- /dev/null +++ b/src/core-sdk-tasks/CalculateTemplateVersions.cs @@ -0,0 +1,126 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using NuGet.Versioning; + +namespace Microsoft.DotNet.Cli.Build +{ + public class CalculateTemplateVersions : Task + { + // Group BundledTemplates by TemplateFrameworkVersion + // In each group, get the version of the template with UseVersionForTemplateInstallPath=true + // From that version number, get the BundledTemplateInstallPath, BundledTemplateMajorMinorVersion, and BundledTemplateMajorMinorPatchVersion + [Required] + public ITaskItem [] BundledTemplates { get; set; } + + [Required] + public string FullNugetVersion { get; set; } + + [Required] + public string ProductMonikerRid { get; set; } + + public string InstallerExtension { get; set; } + + [Required] + public int CombinedBuildNumberAndRevision { get; set; } + + // Should be the BundledTemplates with BundledTemplateInstallPath metadata set to the value calculated for that group + [Output] + public ITaskItem [] BundledTemplatesWithInstallPaths { get; set; } + + // For each group of templates (grouped by TemplateFrameworkVersion), this should be the following + // ItemSpec: NetCore60Templates + // TemplateBaseFilename: dotnet-60templates + // TemplatesMajorMinorVersion: 6.0 (from BundledTemplateMajorMinorVersion from group) + // InstallerUpgradeCode: Guid generated using GenerateGuidFromName, combining TemplateBaseFilename, FullNugetVersion, ProductMonikerRid, and InstallerExtension + // MSIVersion: Result of calling GenerateMsiVersionFromFullVersion logic with CombinedBuildNumberAndRevision and BundledTemplateMajorMinorPatchVersion from template group + [Output] + public ITaskItem [] TemplatesComponents { get; set; } + + private const int PatchVersionResetOffset = 1; + + public override bool Execute() + { + var groups = BundledTemplates.GroupBy(bt => bt.GetMetadata("TemplateFrameworkVersion")) + .ToDictionary(g => g.Key, g => + { + var itemWithVersion = g.SingleOrDefault(i => i.GetMetadata("UseVersionForTemplateInstallPath").Equals("true", StringComparison.OrdinalIgnoreCase)); + if (itemWithVersion == null) + { + throw new InvalidOperationException("Could not find single item with UseVersionForTemplateInstallPath for templates with TemplateFrameworkVersion: " + g.Key); + } + + return Calculate(itemWithVersion.GetMetadata("PackageVersion")); + }); + + BundledTemplatesWithInstallPaths = BundledTemplates.Select(t => + { + var templateWithInstallPath = new TaskItem(t); + templateWithInstallPath.SetMetadata("BundledTemplateInstallPath", groups[t.GetMetadata("TemplateFrameworkVersion")].InstallPath); + return templateWithInstallPath; + }).ToArray(); + + TemplatesComponents = groups.Select(g => + { + string majorMinorWithoutDots = g.Value.MajorMinorVersion.Replace(".", ""); + var componentItem = new TaskItem($"NetCore{majorMinorWithoutDots}Templates"); + var templateBaseFilename = $"dotnet-{majorMinorWithoutDots}templates"; + componentItem.SetMetadata("TemplateBaseFilename", templateBaseFilename); + componentItem.SetMetadata("TemplatesMajorMinorVersion", g.Value.MajorMinorVersion); + var installerUpgradeCode = GenerateGuidFromName.GenerateGuid(string.Join("-", templateBaseFilename, FullNugetVersion, ProductMonikerRid) + InstallerExtension).ToString().ToUpper(); + componentItem.SetMetadata("InstallerUpgradeCode", installerUpgradeCode); + componentItem.SetMetadata("MSIVersion", GenerateMsiVersionFromFullVersion.GenerateMsiVersion(CombinedBuildNumberAndRevision, g.Value.MajorMinorPatchVersion)); + + var brandName = System.Version.Parse(g.Key).Major >= 5 ? + $"Microsoft .NET {g.Key} Templates" : + $"Microsoft .NET Core {g.Key} Templates"; + + componentItem.SetMetadata("BrandNameWithoutVersion", brandName); + + return componentItem; + }).ToArray(); + + return true; + } + + public static BundledTemplate Calculate(string aspNetCorePackageVersionTemplate) + { + var aspNetCoreTemplate = NuGetVersion.Parse(aspNetCorePackageVersionTemplate); + NuGetVersion baseMajorMinorPatch = GetBaseMajorMinorPatch(aspNetCoreTemplate); + string bundledTemplateInstallPath = aspNetCoreTemplate.IsPrerelease + ? $"{baseMajorMinorPatch.Major}.{baseMajorMinorPatch.Minor}.{baseMajorMinorPatch.Patch}-{aspNetCoreTemplate.Release}" + : $"{baseMajorMinorPatch.Major}.{baseMajorMinorPatch.Minor}.{baseMajorMinorPatch.Patch}"; + + return new BundledTemplate + { + InstallPath = bundledTemplateInstallPath, + MajorMinorVersion = $"{baseMajorMinorPatch.Major}.{baseMajorMinorPatch.Minor}", + MajorMinorPatchVersion = $"{baseMajorMinorPatch.Major}.{baseMajorMinorPatch.Minor}.{baseMajorMinorPatch.Patch}" + }; + } + + private static NuGetVersion GetBaseMajorMinorPatch(NuGetVersion aspNetCoreTemplate) + { + // due to historical bug https://github.com/dotnet/core-sdk/issues/6243 + // we need to increase patch version by one in order to "reset" existing install ComponentId + // more in the above bug's detail. + // There is no non-deterministic existing ComponentId under Major version 5. + // so only apply the patch bump when below 5 + int basePatch = aspNetCoreTemplate.Major < 5 ? + aspNetCoreTemplate.Patch + PatchVersionResetOffset : + aspNetCoreTemplate.Patch; + return new NuGetVersion(aspNetCoreTemplate.Major, aspNetCoreTemplate.Minor, basePatch); + } + } + + public class BundledTemplate + { + public string InstallPath { get; set; } + public string MajorMinorVersion { get; set; } + public string MajorMinorPatchVersion { get; set; } + } +} diff --git a/src/core-sdk-tasks/Chmod.cs b/src/core-sdk-tasks/Chmod.cs new file mode 100644 index 000000000..f4133c03d --- /dev/null +++ b/src/core-sdk-tasks/Chmod.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Cli.Build +{ + public class Chmod : ToolTask + { + [Required] + public string Glob { get; set; } + + [Required] + public string Mode { get; set; } + + public bool Recursive { get; set; } + + protected override string ToolName => "chmod"; + + protected override MessageImportance StandardOutputLoggingImportance => MessageImportance.High; + + protected override string GenerateFullPathToTool() => "chmod"; + + protected override string GenerateCommandLineCommands() => $"{GetRecursive()} {GetMode()} {GetGlob()}"; + + private string GetGlob() => Glob; + + private string GetMode() => Mode; + + private string GetRecursive() => Recursive ? "--recursive" : null; + } +} diff --git a/src/core-sdk-tasks/CollatePackageDownloads.cs b/src/core-sdk-tasks/CollatePackageDownloads.cs new file mode 100644 index 000000000..e37de4406 --- /dev/null +++ b/src/core-sdk-tasks/CollatePackageDownloads.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Cli.Build +{ + // Multiple PackageDownload items for the same package are not supported. Rather, to download multiple versions of the same + // package, the PackageDownload items can have a semicolon-separated list of versions (each in brackets) as the Version metadata. + // So this task groups a list of items with PackageVersion metadata into a list of items which can be used as PackageDownloads + public class CollatePackageDownloads : Task + { + [Required] + public ITaskItem[] Packages { get; set; } + + [Output] + public ITaskItem [] PackageDownloads { get; set; } + + public override bool Execute() + { + PackageDownloads = Packages.GroupBy(p => p.ItemSpec) + .Select(g => + { + var packageDownloadItem = new TaskItem(g.Key); + packageDownloadItem.SetMetadata("Version", string.Join(";", + g.Select(p => "[" + p.GetMetadata("PackageVersion") + "]"))); + return packageDownloadItem; + }).ToArray(); + + return true; + } + } +} diff --git a/src/core-sdk-tasks/Crossgen.cs b/src/core-sdk-tasks/Crossgen.cs new file mode 100644 index 000000000..4c11dc74f --- /dev/null +++ b/src/core-sdk-tasks/Crossgen.cs @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Build.Tasks +{ + public sealed class Crossgen : ToolTask + { + public Crossgen() + { + // Disable partial NGEN to avoid excess JIT-compilation. + // The intention is to pre-compile as much as possible. + EnvironmentVariables = new string[] { "COMPlus_PartialNGen=0" }; + } + + [Required] + public string SourceAssembly { get;set; } + + [Required] + public string DestinationPath { get; set; } + + [Required] + public string Architecture { get; set; } + + public string CrossgenPath { get; set; } + + public bool CreateSymbols { get; set; } + + public bool ReadyToRun { get; set; } + + public ITaskItem[] PlatformAssemblyPaths { get; set; } + + private string TempOutputPath { get; set; } + + protected override bool ValidateParameters() + { + base.ValidateParameters(); + + if (!File.Exists(SourceAssembly)) + { + Log.LogError($"SourceAssembly '{SourceAssembly}' does not exist."); + + return false; + } + + return true; + } + + public override bool Execute() + { + string tempDirPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirPath); + TempOutputPath = Path.Combine(tempDirPath, Path.GetFileName(DestinationPath)); + + var toolResult = base.Execute(); + + if (toolResult) + { + var files = Directory.GetFiles(Path.GetDirectoryName(TempOutputPath)); + var destination = Path.GetDirectoryName(DestinationPath); + // Copy both dll and pdb files to the destination folder + foreach(var file in files) + { + File.Copy(file, Path.Combine(destination, Path.GetFileName(file)), overwrite: true); + // Delete file in temp + File.Delete(file); + } + } + + if (File.Exists(TempOutputPath)) + { + File.Delete(TempOutputPath); + } + Directory.Delete(tempDirPath); + + return toolResult; + } + + protected override string ToolName => "crossgen2"; + + // Default is low, but we want to see output at normal verbosity. + protected override MessageImportance StandardOutputLoggingImportance => MessageImportance.Normal; + + // This turns stderr messages into msbuild errors below. + protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High; + + protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) + { + // Crossgen's error/warning formatting is inconsistent and so we do + // not use the "canonical error format" handling of base. + // + // Furthermore, we don't want to log crossgen warnings as msbuild + // warnings because we cannot prevent them and they are only + // occasionally formatted as something that base would recognize as + // a canonically formatted warning anyway. + // + // One thing that is consistent is that crossgen errors go to stderr + // and everything else goes to stdout. Above, we set stderr to high + // importance above, and stdout to normal. So we can use that here + // to distinguish between errors and messages. + if (messageImportance == MessageImportance.High) + { + Log.LogError(singleLine); + } + else + { + Log.LogMessage(messageImportance, singleLine); + } + } + + protected override string GenerateFullPathToTool() => CrossgenPath ?? "crossgen2"; + + protected override string GenerateCommandLineCommands() => $"{GetInPath()} {GetOutPath()} {GetArchitecture()} {GetPlatformAssemblyPaths()} {GetCreateSymbols()}"; + + private string GetArchitecture() => $"--targetarch {Architecture}"; + + private string GetCreateSymbols() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "--pdb" : "--perfmap"; + + private string GetInPath() => $"\"{SourceAssembly}\""; + + private string GetOutPath() => $"-o \"{TempOutputPath}\""; + + private string GetPlatformAssemblyPaths() + { + var platformAssemblyPaths = string.Empty; + if (PlatformAssemblyPaths != null) + { + foreach (var excludeTaskItem in PlatformAssemblyPaths) + { + platformAssemblyPaths += $"-r {excludeTaskItem.ItemSpec}{Path.DirectorySeparatorChar}*.dll "; + } + } + + return platformAssemblyPaths; + } + + protected override void LogToolCommand(string message) => base.LogToolCommand($"{GetWorkingDirectory()}> {message}"); + } +} diff --git a/src/core-sdk-tasks/DotNetDebTool.cs b/src/core-sdk-tasks/DotNetDebTool.cs new file mode 100644 index 000000000..d7f4fd712 --- /dev/null +++ b/src/core-sdk-tasks/DotNetDebTool.cs @@ -0,0 +1,77 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Cli.Build +{ + public class DotNetDebTool : ToolTask + { + [Required] + public string InputDirectory { get; set; } + + [Required] + public string OutputDirectory { get; set; } + + [Required] + public string PackageName { get; set; } + + [Required] + public string PackageVersion { get; set; } + + public string WorkingDirectory { get; set; } + + protected override string ToolName => "package_tool.sh"; + + private string GetInputDir() => $"-i {InputDirectory}"; + + private string GetOutputFile() => $"-o {OutputDirectory}"; + + private string GetPackageName() => $"-n {PackageName}"; + + private string GetPackageVersion() => $"-v {PackageVersion}"; + + protected override MessageImportance StandardOutputLoggingImportance => MessageImportance.High; + + protected override string GenerateFullPathToTool() + { + // if ToolPath was not provided by the MSBuild script + if (string.IsNullOrEmpty(ToolPath)) + { + Log.LogError($"Could not find the Path to {ToolName}"); + + return string.Empty; + } + + return ToolPath; + } + + protected override string GetWorkingDirectory() => WorkingDirectory ?? base.GetWorkingDirectory(); + + protected override string GenerateCommandLineCommands() + { + var commandLineCommands = $"{GetInputDir()} {GetOutputFile()} {GetPackageName()} {GetPackageVersion()}"; + + LogToolCommand($"package_tool.sh {commandLineCommands}"); + + return commandLineCommands; + } + + protected override void LogToolCommand(string message) => base.LogToolCommand($"{GetWorkingDirectory()}> {message}"); + + protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) => Log.LogMessage(messageImportance, singleLine, null); + + protected override ProcessStartInfo GetProcessStartInfo(string pathToTool, string commandLineCommands, string responseFileSwitch) + { + var psi = base.GetProcessStartInfo(pathToTool, commandLineCommands, responseFileSwitch); + foreach (var environmentVariableName in new EnvironmentFilter().GetEnvironmentVariableNamesToRemove()) + { + psi.Environment.Remove(environmentVariableName); + } + + return psi; + } + } +} diff --git a/src/core-sdk-tasks/DotNetTool.cs b/src/core-sdk-tasks/DotNetTool.cs new file mode 100644 index 000000000..2a7c834b4 --- /dev/null +++ b/src/core-sdk-tasks/DotNetTool.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.DotNet.Cli.Build.Framework; + +namespace Microsoft.DotNet.Cli.Build +{ + public abstract class DotNetTool : ToolTask + { + public DotNetTool() + { + } + + protected abstract string Command { get; } + + protected abstract string Args { get; } + + protected override ProcessStartInfo GetProcessStartInfo(string pathToTool, string commandLineCommands, string responseFileSwitch) + { + var psi = base.GetProcessStartInfo(pathToTool, commandLineCommands, responseFileSwitch); + foreach (var environmentVariableName in new EnvironmentFilter().GetEnvironmentVariableNamesToRemove()) + { + psi.Environment.Remove(environmentVariableName); + } + + return psi; + } + + public string WorkingDirectory { get; set; } + + protected override string ToolName => $"dotnet{(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty)}"; + + protected override MessageImportance StandardOutputLoggingImportance => MessageImportance.High; + + protected override string GenerateFullPathToTool() + { + // if ToolPath was not provided by the MSBuild script + if (string.IsNullOrEmpty(ToolPath)) + { + Log.LogError($"Could not find the Path to {ToolName}"); + + return string.Empty; + } + + return ToolPath; + } + + protected override string GetWorkingDirectory() => WorkingDirectory ?? base.GetWorkingDirectory(); + + protected override string GenerateCommandLineCommands() + { + var commandLineCommands = $"{Command} {Args}"; + + LogToolCommand($"[DotNetTool] {commandLineCommands}"); + + return commandLineCommands; + } + + protected override void LogToolCommand(string message) => base.LogToolCommand($"{GetWorkingDirectory()}> {message}"); + } +} diff --git a/src/core-sdk-tasks/Enumerations/BuildPlatform.cs b/src/core-sdk-tasks/Enumerations/BuildPlatform.cs new file mode 100644 index 000000000..72e68a0dc --- /dev/null +++ b/src/core-sdk-tasks/Enumerations/BuildPlatform.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.Cli.Build.Framework +{ + public enum BuildPlatform + { + Windows = 1, + Unix = 2, + Linux = 3, + OSX = 4, + Ubuntu = 5, + CentOS = 6, + RHEL = 7, + Debian = 8, + Fedora = 9, + OpenSuse = 10, + FreeBSD = 11 + } +} diff --git a/src/core-sdk-tasks/EnvironmentVariableFilter.cs b/src/core-sdk-tasks/EnvironmentVariableFilter.cs new file mode 100644 index 000000000..6b82fa32c --- /dev/null +++ b/src/core-sdk-tasks/EnvironmentVariableFilter.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.DotNet.Cli.Build +{ + public class EnvironmentFilter + { + private const string MSBuildEnvironmentVariablePrefix = "MSBuild"; + private const string DotNetEnvironmentVariablePrefix = "DOTNET"; + private const string NugetEnvironmentVariablePrefix = "NUGET"; + + private IEnumerable _prefixesOfEnvironmentVariablesToRemove = new string [] + { + MSBuildEnvironmentVariablePrefix, + DotNetEnvironmentVariablePrefix, + NugetEnvironmentVariablePrefix + }; + + private IEnumerable _environmentVariablesToRemove = new string [] + { + "CscToolExe", "VbcToolExe" + }; + + private IEnumerable _environmentVariablesToKeep = new string [] + { + "DOTNET_CLI_TELEMETRY_SESSIONID", + "DOTNET_CLI_UI_LANGUAGE", + "DOTNET_MULTILEVEL_LOOKUP", + "DOTNET_RUNTIME_ID", + "NUGET_PACKAGES" + }; + + public IEnumerable GetEnvironmentVariableNamesToRemove() + { + var allEnvironmentVariableNames = Environment + .GetEnvironmentVariables() + .Keys + .Cast(); + + var environmentVariablesToRemoveByPrefix = allEnvironmentVariableNames + .Where(e => _prefixesOfEnvironmentVariablesToRemove.Any(p => e.StartsWith(p))); + + var environmentVariablesToRemoveByName = allEnvironmentVariableNames + .Where(e => _environmentVariablesToRemove.Contains(e)); + + var environmentVariablesToRemove = environmentVariablesToRemoveByName + .Concat(environmentVariablesToRemoveByPrefix) + .Distinct() + .Except(_environmentVariablesToKeep); + + return environmentVariablesToRemove; + } + } +} diff --git a/src/core-sdk-tasks/ExtractArchiveToDirectory.cs b/src/core-sdk-tasks/ExtractArchiveToDirectory.cs new file mode 100644 index 000000000..d7836d7e1 --- /dev/null +++ b/src/core-sdk-tasks/ExtractArchiveToDirectory.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +#if !NETFRAMEWORK +using System.Formats.Tar; +#endif +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Microsoft.DotNet.Build.Tasks +{ + /// + /// Extracts a .zip or .tar.gz file to a directory. + /// + public sealed class ExtractArchiveToDirectory : ToolTask + { + /// + /// The path to the archive to extract. + /// + [Required] + public string SourceArchive { get; set; } + + /// + /// The path of the directory to which the archive should be extracted. + /// + [Required] + public string DestinationDirectory { get; set; } + + /// + /// Indicates if the destination directory should be cleaned if it already exists. + /// + public bool CleanDestination { get; set; } + + /// + /// A list of directories, relative to the root of the archive to include. If empty all directories will be copied. + /// + public ITaskItem[] DirectoriesToCopy { get; set; } + + protected override bool ValidateParameters() + { + base.ValidateParameters(); + + var retVal = true; + + if (Directory.Exists(DestinationDirectory) && CleanDestination == true) + { + Log.LogMessage(MessageImportance.Low, "'{0}' already exists, trying to delete before unzipping...", DestinationDirectory); + Directory.Delete(DestinationDirectory, recursive: true); + } + + if (!File.Exists(SourceArchive)) + { + Log.LogError($"SourceArchive '{SourceArchive} does not exist."); + + retVal = false; + } + + if (retVal) + { + Log.LogMessage($"Creating Directory {DestinationDirectory}"); + Directory.CreateDirectory(DestinationDirectory); + } + + return retVal; + } + + public override bool Execute() + { + bool retVal = true; + bool isZipArchive = Path.GetExtension(SourceArchive).Equals(".zip", StringComparison.OrdinalIgnoreCase); + bool isTarballArchive = SourceArchive.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase); + + // Inherits from ToolTask in order to shell out to tar for complete extraction + // If the file is a .zip, then don't call the base Execute method, just run as a normal task + // If the file is a .tar.gz, and DirectoriesToCopy isn't empty, also run a normal task. + if (isZipArchive || isTarballArchive) + { + if (ValidateParameters()) + { + if (DirectoriesToCopy != null && DirectoriesToCopy.Length != 0) + { + // Partial archive extraction + if (isZipArchive) + { + var zip = new ZipArchive(File.OpenRead(SourceArchive)); + string loc = DestinationDirectory; + foreach (var entry in zip.Entries) + { + if (ShouldExtractItem(entry.FullName)) + { + if (!Directory.Exists(Path.Combine(DestinationDirectory, Path.GetDirectoryName(entry.FullName)))) + { + Directory.CreateDirectory(Path.Combine(DestinationDirectory, Path.GetDirectoryName(entry.FullName))); + } + + Log.LogMessage(Path.GetDirectoryName(entry.FullName)); + entry.ExtractToFile(Path.Combine(loc, entry.FullName)); + } + } + } + else + { +#if NETFRAMEWORK + // Run the base tool, which uses external 'tar' command + retVal = base.Execute(); +#else + // Decompress GZip content + using FileStream compressedFileStream = File.OpenRead(SourceArchive); + using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress); + using var decompressedStream = new MemoryStream(); + decompressor.CopyTo(decompressedStream); + decompressedStream.Seek(0, SeekOrigin.Begin); + + // Extract Tar content + using TarReader tr = new TarReader(decompressedStream); + while (tr.GetNextEntry() is TarEntry tarEntry) + { + if (tarEntry.EntryType != TarEntryType.Directory) + { + string entryName = tarEntry.Name; + entryName = entryName.StartsWith("./") ? entryName[2..] : entryName; + if (ShouldExtractItem(entryName)) + { + Log.LogMessage(entryName); + string destinationPath = Path.Combine(DestinationDirectory, entryName); + Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); + tarEntry.ExtractToFile(destinationPath, overwrite: true); + } + } + } +#endif + } + } + else + { + // Complete archive extraction + if (isZipArchive) + { +#if NETFRAMEWORK + // .NET Framework doesn't have overload to overwrite files + ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory); +#else + + ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory, overwriteFiles: true); +#endif + } + else + { + // Run the base tool, which uses external 'tar' command + retVal = base.Execute(); + } + } + } + else + { + retVal = false; + } + } + else + { + retVal = base.Execute(); + } + + if (!retVal) + { + Log.LogMessage($"Deleting Directory {DestinationDirectory}"); + Directory.Delete(DestinationDirectory); + } + + return retVal; + } + + private bool ShouldExtractItem(string path) => DirectoriesToCopy?.Any(p => path.StartsWith(p.ItemSpec)) ?? false; + + protected override string ToolName => "tar"; + + protected override MessageImportance StandardOutputLoggingImportance => MessageImportance.High; + + protected override string GenerateFullPathToTool() => "tar"; + + protected override string GenerateCommandLineCommands() => $"xf {SourceArchive} -C {DestinationDirectory}"; + } +} diff --git a/src/core-sdk-tasks/GenerateDefaultRuntimeFrameworkVersion.cs b/src/core-sdk-tasks/GenerateDefaultRuntimeFrameworkVersion.cs new file mode 100644 index 000000000..6c9cccd23 --- /dev/null +++ b/src/core-sdk-tasks/GenerateDefaultRuntimeFrameworkVersion.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using NuGet.Versioning; + +namespace Microsoft.DotNet.Cli.Build +{ + public class GenerateDefaultRuntimeFrameworkVersion : Task + { + [Required] + public string RuntimePackVersion { get; set; } + + [Output] + public string DefaultRuntimeFrameworkVersion { get; set; } + + public override bool Execute() + { + if (NuGetVersion.TryParse(RuntimePackVersion, out var version)) + { + DefaultRuntimeFrameworkVersion = version.IsPrerelease && version.Patch == 0 ? + RuntimePackVersion : + new NuGetVersion(version.Major, version.Minor, 0).ToFullString(); + + return true; + } + + return false; + } + } +} diff --git a/src/core-sdk-tasks/GenerateGuidFromName.cs b/src/core-sdk-tasks/GenerateGuidFromName.cs new file mode 100644 index 000000000..0b9e46120 --- /dev/null +++ b/src/core-sdk-tasks/GenerateGuidFromName.cs @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Security.Cryptography; + +namespace Microsoft.DotNet.Cli.Build +{ + public class GenerateGuidFromName + { + // Generate a Version 5 (SHA1 Name Based) Guid from a name. + public static Guid GenerateGuid(string name) + { + // Any fixed GUID will do for a namespace. + Guid namespaceId = new Guid("28F1468D-672B-489A-8E0C-7C5B3030630C"); + + using (SHA1 hasher = SHA1.Create()) + { + var nameBytes = System.Text.Encoding.UTF8.GetBytes(name ?? string.Empty); + var namespaceBytes = namespaceId.ToByteArray(); + + SwapGuidByteOrder(namespaceBytes); + + var streamToHash = new byte[namespaceBytes.Length + nameBytes.Length]; + + Array.Copy(namespaceBytes, streamToHash, namespaceBytes.Length); + Array.Copy(nameBytes, 0, streamToHash, namespaceBytes.Length, nameBytes.Length); + + var hashResult = hasher.ComputeHash(streamToHash); + + var res = new byte[16]; + + Array.Copy(hashResult, res, res.Length); + + unchecked { res[6] = (byte)(0x50 | (res[6] & 0x0F)); } + unchecked { res[8] = (byte)(0x40 | (res[8] & 0x3F)); } + + SwapGuidByteOrder(res); + + return new Guid(res); + } + } + + // Do a byte order swap, .NET GUIDs store multi byte components in little + // endian. + private static void SwapGuidByteOrder(byte[] b) + { + Swap(b, 0, 3); + Swap(b, 1, 2); + Swap(b, 5, 6); + Swap(b, 7, 8); + } + + private static void Swap(byte[] b, int x, int y) + { + byte t = b[x]; + b[x] = b[y]; + b[y] = t; + } + } +} diff --git a/src/core-sdk-tasks/GenerateMSBuildExtensionsSWR.cs b/src/core-sdk-tasks/GenerateMSBuildExtensionsSWR.cs new file mode 100644 index 000000000..bac9ad0d8 --- /dev/null +++ b/src/core-sdk-tasks/GenerateMSBuildExtensionsSWR.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace Microsoft.DotNet.Cli.Build +{ + public class GenerateMSBuildExtensionsSWR : Task + { + [Required] + public string MSBuildExtensionsLayoutDirectory { get; set; } + + [Required] + public string OutputFile { get; set; } + + public override bool Execute() + { + StringBuilder sb = new StringBuilder(SWR_HEADER); + + AddFolder(sb, + @"MSBuildSdkResolver", + @"MSBuild\Current\Bin\SdkResolvers\Microsoft.DotNet.MSBuildSdkResolver", + ngenAssemblies: true); + + AddFolder(sb, + @"msbuildExtensions", + @"MSBuild"); + + AddFolder(sb, + @"msbuildExtensions-ver", + @"MSBuild\Current"); + + File.WriteAllText(OutputFile, sb.ToString()); + + return true; + } + + private void AddFolder(StringBuilder sb, string relativeSourcePath, string swrInstallDir, bool ngenAssemblies = false) + { + string sourceFolder = Path.Combine(MSBuildExtensionsLayoutDirectory, relativeSourcePath); + var files = Directory.GetFiles(sourceFolder) + .Where(f => !Path.GetExtension(f).Equals(".pdb", StringComparison.OrdinalIgnoreCase) && !Path.GetExtension(f).Equals(".swr", StringComparison.OrdinalIgnoreCase)) + .ToList(); + if (files.Any(f => !Path.GetFileName(f).Equals("_._"))) + { + sb.Append(@"folder ""InstallDir:\"); + sb.Append(swrInstallDir); + sb.AppendLine(@"\"""); + + foreach (var file in files) + { + sb.Append(@" file source=""$(PkgVS_Redist_Common_Net_Core_SDK_MSBuildExtensions)\"); + sb.Append(Path.Combine(relativeSourcePath, Path.GetFileName(file))); + sb.Append('"'); + + if (ngenAssemblies && file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + sb.Append(@" vs.file.ngenApplications=""[installDir]\Common7\IDE\vsn.exe"""); + } + + sb.AppendLine(); + } + + sb.AppendLine(); + } + + foreach (var subfolder in Directory.GetDirectories(sourceFolder)) + { + string subfolderName = Path.GetFileName(subfolder); + string newRelativeSourcePath = Path.Combine(relativeSourcePath, subfolderName); + string newSwrInstallDir = Path.Combine(swrInstallDir, subfolderName); + + // Don't propagate ngenAssemblies to subdirectories. + AddFolder(sb, newRelativeSourcePath, newSwrInstallDir); + } + } + + readonly string SWR_HEADER = @"use vs + +package name=Microsoft.Net.Core.SDK.MSBuildExtensions + version=$(ProductsBuildVersion) + vs.package.internalRevision=$(PackageInternalRevision) + +"; + } +} diff --git a/src/core-sdk-tasks/GenerateMsiVersionFromFullVersion.cs b/src/core-sdk-tasks/GenerateMsiVersionFromFullVersion.cs new file mode 100644 index 000000000..72307e4c1 --- /dev/null +++ b/src/core-sdk-tasks/GenerateMsiVersionFromFullVersion.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using NuGet.Versioning; + +namespace Microsoft.DotNet.Cli.Build +{ + public class GenerateMsiVersionFromFullVersion : Task + { + [Required] + public int VersionRevision { get; set; } + + [Required] + public string VersionMajorMinorPatch { get; set; } + + [Output] + public string MsiVersion { get; set; } + + public override bool Execute() + { + MsiVersion = GenerateMsiVersion(VersionRevision, VersionMajorMinorPatch); + + return true; + } + + public static string GenerateMsiVersion(int versionRevision, string versionMajorMinorPatch) + { + var parsedVersion = NuGetVersion.Parse(versionMajorMinorPatch); + + var buildVersion = new Version() + { + Major = parsedVersion.Major, + Minor = parsedVersion.Minor, + Patch = parsedVersion.Patch, + VersionRevision = versionRevision + }; + + return buildVersion.GenerateMsiVersion(); + } + } +} diff --git a/src/core-sdk-tasks/GenerateSdkRuntimeIdentifierChain.cs b/src/core-sdk-tasks/GenerateSdkRuntimeIdentifierChain.cs new file mode 100644 index 000000000..4b805e1da --- /dev/null +++ b/src/core-sdk-tasks/GenerateSdkRuntimeIdentifierChain.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using NuGet.RuntimeModel; + +namespace Microsoft.DotNet.Cli.Build +{ + public class GenerateSdkRuntimeIdentifierChain : Task + { + [Required] + public string RuntimeIdentifier { get; set; } + + [Required] + public string RuntimeIdentifierGraphPath { get; set; } + + [Required] + public string RuntimeIdentifierChainOutputPath { get; set; } + + public override bool Execute() + { + var runtimeIdentifierGraph = JsonRuntimeFormat.ReadRuntimeGraph(RuntimeIdentifierGraphPath); + + var chainContents = string.Join("\n", runtimeIdentifierGraph.ExpandRuntime(RuntimeIdentifier)); + File.WriteAllText(RuntimeIdentifierChainOutputPath, chainContents); + + return true; + } + } +} diff --git a/src/core-sdk-tasks/GetDependencyInfo.cs b/src/core-sdk-tasks/GetDependencyInfo.cs new file mode 100644 index 000000000..60b5af107 --- /dev/null +++ b/src/core-sdk-tasks/GetDependencyInfo.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Linq; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Cli.Build +{ + /// + /// Gets version and commit of a dependency by its name + /// from eng/Version.Details.xml + /// + public class GetDependencyInfo : Task + { + [Required] + public string VersionDetailsXmlFile { get; set; } + + [Required] + public string DependencyName { get; set; } + + [Output] + public string DependencyVersion { get; set; } + + [Output] + public string DependencyCommit { get; set; } + + public override bool Execute() + { + try + { + XDocument document = XDocument.Load(VersionDetailsXmlFile); + XElement dependency = document + .Element("Dependencies")? + .Element("ProductDependencies")? + .Elements("Dependency") + .FirstOrDefault(d => DependencyName.Equals(d.Attribute("Name")?.Value)); + + if (dependency != null) + { + DependencyVersion = dependency.Attribute("Version")?.Value; + DependencyCommit = dependency.Element("Sha")?.Value; + } + } + catch (Exception ex) + { + Log.LogWarning($"GetComponentCommit failed for VersionDetailsXmlFile={VersionDetailsXmlFile}, DependencyName={DependencyName}: {ex}"); + } + + return true; + } + } +} diff --git a/src/core-sdk-tasks/GetLinuxNativeInstallerDependencyVersions.cs b/src/core-sdk-tasks/GetLinuxNativeInstallerDependencyVersions.cs new file mode 100644 index 000000000..dda0ffa0c --- /dev/null +++ b/src/core-sdk-tasks/GetLinuxNativeInstallerDependencyVersions.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Cli.Build +{ + public class GetLinuxNativeInstallerDependencyVersions : Task + { + [Required] + public string PackageVersion { get; set; } + + [Output] + public string MajorMinorVersion { get; private set; } + + [Output] + public string MajorMinorPatchVersion { get; private set; } + + [Output] + public string VersionWithTilde { get; private set; } + + public override bool Execute() + { + string[] dotSplit = PackageVersion.Split('.'); + MajorMinorVersion = dotSplit[0] + "." + dotSplit[1]; + + string[] prereleaseSplit = PackageVersion.Split(new[] { '-' }, count: 2); + MajorMinorPatchVersion = prereleaseSplit[0]; + VersionWithTilde = MajorMinorPatchVersion; + + if (prereleaseSplit.Length > 1) + { + VersionWithTilde += "~" + prereleaseSplit[1]; + } + + return true; + } + } +} diff --git a/src/core-sdk-tasks/GetRuntimePackRids.cs b/src/core-sdk-tasks/GetRuntimePackRids.cs new file mode 100644 index 000000000..e337ca633 --- /dev/null +++ b/src/core-sdk-tasks/GetRuntimePackRids.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Newtonsoft.Json.Linq; + +namespace Microsoft.DotNet.Cli.Build +{ + public class GetRuntimePackRids : Task + { + [Required] + public string MetapackagePath { get; set; } + + [Output] + public ITaskItem[] AvailableRuntimePackRuntimeIdentifiers { get; set; } + + public override bool Execute() + { + string runtimeJsonPath = Path.Combine(MetapackagePath, "runtime.json"); + string runtimeJsonContents = File.ReadAllText(runtimeJsonPath); + var runtimeJsonRoot = JObject.Parse(runtimeJsonContents); + string [] runtimeIdentifiers = ((JObject)runtimeJsonRoot["runtimes"]).Properties().Select(p => p.Name).ToArray(); + AvailableRuntimePackRuntimeIdentifiers = runtimeIdentifiers.Select(rid => new TaskItem(rid)).ToArray(); + + return true; + } + } +} diff --git a/src/core-sdk-tasks/ReplaceFileContents.cs b/src/core-sdk-tasks/ReplaceFileContents.cs new file mode 100644 index 000000000..671b73674 --- /dev/null +++ b/src/core-sdk-tasks/ReplaceFileContents.cs @@ -0,0 +1,138 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; + +namespace Microsoft.DotNet.Cli.Build +{ + /// + /// Reads contents of an input file, and searches for each replacement passed in. + /// + /// When ReplacementItems is matched, it will replace the Include/ItemSpec with the corresponding + /// ReplacementString metadata value. This can be useful if the ReplacementString is a value that + /// cannot be represented by ITaskItem.ItemSpec (like string.Empty). + /// + /// When a ReplacementPattern is matched it will replace it with the string of the corresponding (by index) + /// item in ReplacementStrings. + /// + /// For example, if 2 ReplacementPatterns are passed in, 2 ReplacementStrings must also passed in and the first + /// pattern will be replaced with the first string, and the second pattern replaced with the second string. + /// + /// ReplacementPattern could easily be a regex, but it isn't needed for current use cases, so leaving this + /// as just a string that will be replaced. + /// + public class ReplaceFileContents : Task + { + [Required] + public ITaskItem[] InputFiles { get; set; } + + [Required] + public ITaskItem[] DestinationFiles { get; set; } + + public ITaskItem[] ReplacementItems { get; set; } + + public ITaskItem[] ReplacementPatterns { get; set; } + + public ITaskItem[] ReplacementStrings { get; set; } + + /// + /// Gets or sets a string that a file must contain for the replacement to be performed. + /// + public string FileMustContainText { get; set; } + + public override bool Execute() + { + if (ReplacementItems == null && ReplacementPatterns == null && ReplacementStrings == null) + { + throw new Exception($"ReplaceFileContents was called with no replacement values. Either pass ReplacementItems or ReplacementPatterns/ReplacementStrings properties."); + } + + ReplacementItems = ReplacementItems ?? Array.Empty(); + ReplacementPatterns = ReplacementPatterns ?? Array.Empty(); + ReplacementStrings = ReplacementStrings ?? Array.Empty(); + + if (ReplacementPatterns.Length != ReplacementStrings.Length) + { + throw new Exception($"Expected {nameof(ReplacementPatterns)} (length {ReplacementPatterns.Length}) and {nameof(ReplacementStrings)} (length {ReplacementStrings.Length}) to have the same length."); + } + + if (InputFiles.Length != DestinationFiles.Length) + { + throw new Exception($"Expected {nameof(InputFiles)} (length {InputFiles.Length}) and {nameof(DestinationFiles)} (length {DestinationFiles.Length}) to have the same length."); + } + + var filesNotFound = InputFiles.Where(i => !File.Exists(i.ItemSpec)).Select(i => i.ItemSpec); + if (filesNotFound.Any()) + { + var filesNotFoundString = string.Join(",", filesNotFound); + throw new FileNotFoundException($"Expected files where not found: {filesNotFoundString}"); + } + + Log.LogMessage(MessageImportance.High, $"ReplacingContents for `{InputFiles.Length}` files."); + + for (var i = 0; i < InputFiles.Length; i++) + { + ReplaceContents(InputFiles[i].ItemSpec, DestinationFiles[i].ItemSpec); + } + + return true; + } + + public void ReplaceContents(string inputFile, string destinationFile) + { + string inputFileText = File.ReadAllText(inputFile); + if (!string.IsNullOrEmpty(FileMustContainText) && !inputFileText.Contains(FileMustContainText)) + { + Log.LogMessage(MessageImportance.Low, $"Skipping replacement on `{inputFile}` because it does not contain the text '{FileMustContainText}'."); + return; + } + + string outputFileText = ReplacePatterns(inputFileText); + + WriteOutputFile(destinationFile, outputFileText); + } + + public string ReplacePatterns(string inputFileText) + { + var outText = inputFileText; + + foreach (var replacementItem in ReplacementItems) + { + var replacementPattern = replacementItem.ItemSpec; + var replacementString = replacementItem.GetMetadata("ReplacementString"); + + outText = outText.Replace(replacementPattern, replacementString); + } + + for (int i=0; i + /// Replaces files that have the same content with hard links. + /// + public sealed class ReplaceFilesWithSymbolicLinks : Task + { + /// + /// The path to the directory to recursively search for files to replace with symbolic links. + /// + [Required] + public string Directory { get; set; } = ""; + + /// + /// The path to the directory with files to link to. + /// + [Required] + public string LinkToFilesFrom { get; set; } = ""; + +#if NETFRAMEWORK + public override bool Execute() + { + Log.LogError($"{nameof(ReplaceFilesWithSymbolicLinks)} is not supported on .NET Framework."); + return false; + } +#else + public override bool Execute() + { + if (OperatingSystem.IsWindows()) + { + Log.LogError($"{nameof(ReplaceFilesWithSymbolicLinks)} is not supported on Windows."); + return false; + } + + if (!System.IO.Directory.Exists(Directory)) + { + Log.LogError($"'{Directory}' does not exist."); + return false; + } + + if (!System.IO.Directory.Exists(LinkToFilesFrom)) + { + Log.LogError($"'{LinkToFilesFrom}' does not exist."); + return false; + } + + // Find all non-empty, non-symbolic link files. + string[] files = new FileSystemEnumerable( + Directory, + (ref FileSystemEntry entry) => entry.ToFullPath(), + new EnumerationOptions() + { + AttributesToSkip = FileAttributes.ReparsePoint, + RecurseSubdirectories = true + }) + { + ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory + && entry.Length > 0 + }.ToArray(); + + foreach (var file in files) + { + string fileName = Path.GetFileName(file); + + // Look for a file with the same name in LinkToFilesFrom + // and replace it with a symbolic link if it has the same content. + string targetFile = Path.Combine(LinkToFilesFrom, fileName); + if (File.Exists(targetFile) && FilesHaveSameContent(file, targetFile)) + { + ReplaceByLinkTo(file, targetFile); + } + } + + return true; + } + + private unsafe bool FilesHaveSameContent(string path1, string path2) + { + using var mappedFile1 = MemoryMappedFile.CreateFromFile(path1, FileMode.Open); + using var accessor1 = mappedFile1.CreateViewAccessor(); + byte* ptr1 = null; + + using var mappedFile2 = MemoryMappedFile.CreateFromFile(path2, FileMode.Open); + using var accessor2 = mappedFile2.CreateViewAccessor(); + byte* ptr2 = null; + + try + { + accessor1.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr1); + Span span1 = new Span(ptr1, checked((int)accessor1.SafeMemoryMappedViewHandle.ByteLength)); + + accessor2.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr2); + Span span2 = new Span(ptr2, checked((int)accessor2.SafeMemoryMappedViewHandle.ByteLength)); + + return span1.SequenceEqual(span2); + } + finally + { + if (ptr1 != null) + { + accessor1.SafeMemoryMappedViewHandle.ReleasePointer(); + ptr1 = null; + } + if (ptr2 != null) + { + accessor2.SafeMemoryMappedViewHandle.ReleasePointer(); + ptr2 = null; + } + } + } + + void ReplaceByLinkTo(string path, string pathToTarget) + { + // To link, the target mustn't exist. Make a backup, so we can restore it when linking fails. + string backupFile = $"{path}.pre_link_backup"; + File.Move(path, backupFile); + + try + { + string relativePath = Path.GetRelativePath(Path.GetDirectoryName(path)!, pathToTarget); + File.CreateSymbolicLink(path, relativePath); + + File.Delete(backupFile); + + Log.LogMessage(MessageImportance.Normal, $"Linked '{path}' to '{relativePath}'."); + } + catch (Exception ex) + { + Log.LogError($"Unable to link '{path}' to '{pathToTarget}.': {ex}"); + + File.Move(backupFile, path); + + throw; + } + } +#endif + } +} diff --git a/src/core-sdk-tasks/TarGzFileCreateFromDirectory.cs b/src/core-sdk-tasks/TarGzFileCreateFromDirectory.cs new file mode 100644 index 000000000..f239e0161 --- /dev/null +++ b/src/core-sdk-tasks/TarGzFileCreateFromDirectory.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Build.Tasks +{ + public sealed class TarGzFileCreateFromDirectory : ToolTask + { + /// + /// The path to the directory to be archived. + /// + [Required] + public string SourceDirectory { get; set; } + + /// + /// The path of the archive to be created. + /// + [Required] + public string DestinationArchive { get; set; } + + /// + /// Indicates if the destination archive should be overwritten if it already exists. + /// + public bool OverwriteDestination { get; set; } + + /// + /// If zipping an entire folder without exclusion patterns, whether to include the folder in the archive. + /// + public bool IncludeBaseDirectory { get; set; } + + /// + /// An item group of regular expressions for content to exclude from the archive. + /// + public ITaskItem[] ExcludePatterns { get; set; } + + public bool IgnoreExitCode { get; set; } + + protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) + { + int returnCode = base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands); + + if (IgnoreExitCode) + { + returnCode = 0; + } + + return returnCode; + } + + protected override bool ValidateParameters() + { + base.ValidateParameters(); + + var retVal = true; + + if (File.Exists(DestinationArchive)) + { + if (OverwriteDestination == true) + { + Log.LogMessage(MessageImportance.Low, $"{DestinationArchive} will be overwritten"); + } + else + { + Log.LogError($"'{DestinationArchive}' already exists. Did you forget to set '{nameof(OverwriteDestination)}' to true?"); + + retVal = false; + } + } + + SourceDirectory = Path.GetFullPath(SourceDirectory); + + SourceDirectory = SourceDirectory.EndsWith(Path.DirectorySeparatorChar.ToString()) + ? SourceDirectory + : SourceDirectory + Path.DirectorySeparatorChar; + + if (!Directory.Exists(SourceDirectory)) + { + Log.LogError($"SourceDirectory '{SourceDirectory} does not exist."); + + retVal = false; + } + + return retVal; + } + + public override bool Execute() => base.Execute(); + + protected override string ToolName => "tar"; + + protected override MessageImportance StandardOutputLoggingImportance => MessageImportance.High; + + protected override string GenerateFullPathToTool() => "tar"; + + protected override string GenerateCommandLineCommands() => $"{GetDestinationArchive()} {GetSourceSpecification()}"; + + private string GetSourceSpecification() + { + if (IncludeBaseDirectory) + { + var parentDirectory = Directory.GetParent(SourceDirectory).Parent.FullName; + var sourceDirectoryName = Path.GetFileName(Path.GetDirectoryName(SourceDirectory)); + return $"--directory {parentDirectory} {sourceDirectoryName} {GetExcludes()}"; + } + else + { + return $"--directory {SourceDirectory} {GetExcludes()} \".\""; + } + } + + private string GetDestinationArchive() => $"-czf {DestinationArchive}"; + + private string GetExcludes() + { + var excludes = string.Empty; + + if (ExcludePatterns != null) + { + foreach (var excludeTaskItem in ExcludePatterns) + { + excludes += $" --exclude {excludeTaskItem.ItemSpec}"; + } + } + + return excludes; + } + + protected override void LogToolCommand(string message) => base.LogToolCommand($"{GetWorkingDirectory()}> {message}"); + } +} diff --git a/src/core-sdk-tasks/UpdateRuntimeConfig.cs b/src/core-sdk-tasks/UpdateRuntimeConfig.cs new file mode 100644 index 000000000..3130f5785 --- /dev/null +++ b/src/core-sdk-tasks/UpdateRuntimeConfig.cs @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Newtonsoft.Json.Linq; + +namespace Microsoft.DotNet.Build.Tasks +{ + public sealed class UpdateRuntimeConfig : Task + { + [Required] + public ITaskItem[] RuntimeConfigPaths { get; set; } + + [Required] + public string MicrosoftNetCoreAppVersion { get; set; } + + [Required] + public string MicrosoftAspNetCoreAppVersion { get; set; } + + public override bool Execute() + { + foreach (var file in RuntimeConfigPaths) + { + UpdateFile(file.ItemSpec); + } + + return true; + } + + private void UpdateFile(string file) + { + var text = File.ReadAllText(file); + JObject config = JObject.Parse(text); + var frameworks = config["runtimeOptions"]?["frameworks"]; + var framework = config["runtimeOptions"]?["framework"]; + if (frameworks != null) + { + foreach (var item in frameworks) + { + UpdateFramework(item); + } + } + else if (framework != null) + { + UpdateFramework(framework); + } + + File.WriteAllText(file, config.ToString()); + } + + private void UpdateFramework(JToken item) + { + var framework = (JObject)item; + var name = framework["name"].Value(); + if (name == "Microsoft.NETCore.App") + { + framework["version"] = MicrosoftNetCoreAppVersion; + } + else if (name == "Microsoft.AspNetCore.App") + { + framework["version"] = MicrosoftAspNetCoreAppVersion; + } + } + } +} \ No newline at end of file diff --git a/src/core-sdk-tasks/Utils/Version.cs b/src/core-sdk-tasks/Utils/Version.cs new file mode 100644 index 000000000..7444ed123 --- /dev/null +++ b/src/core-sdk-tasks/Utils/Version.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.Cli.Build +{ + public class Version + { + public virtual int Major { get; set; } + public virtual int Minor { get; set; } + public virtual int Patch { get; set; } + public virtual int VersionRevision { get; set; } + + public string GenerateMsiVersion() + { + // MSI versioning + // Encode the CLI version to fit into the MSI versioning scheme - https://msdn.microsoft.com/en-us/library/windows/desktop/aa370859(v=vs.85).aspx + // MSI versions are 3 part + // major.minor.build + // Size(bits) of each part 8 8 16 + // So we have 32 bits to encode the CLI version + // Starting with most significant bit this how the CLI version is going to be encoded as MSI Version + // CLI major -> 6 bits + // CLI minor -> 6 bits + // CLI patch -> 6 bits + // CLI VersionRevision -> 14 bits + var major = Major << 26; + var minor = Minor << 20; + var patch = Patch << 14; + var msiVersionNumber = major | minor | patch | VersionRevision; + + var msiMajor = (msiVersionNumber >> 24) & 0xFF; + var msiMinor = (msiVersionNumber >> 16) & 0xFF; + var msiBuild = msiVersionNumber & 0xFFFF; + + return $"{msiMajor}.{msiMinor}.{msiBuild}"; + } + } +} diff --git a/src/core-sdk-tasks/ZipFileCreateFromDirectory.cs b/src/core-sdk-tasks/ZipFileCreateFromDirectory.cs new file mode 100644 index 000000000..cb062e529 --- /dev/null +++ b/src/core-sdk-tasks/ZipFileCreateFromDirectory.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.IO.Compression; +using System.Text.RegularExpressions; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Build.Tasks +{ + public sealed class ZipFileCreateFromDirectory : Task + { + /// + /// The path to the directory to be archived. + /// + [Required] + public string SourceDirectory { get; set; } + + /// + /// The path of the archive to be created. + /// + [Required] + public string DestinationArchive { get; set; } + + /// + /// Indicates if the destination archive should be overwritten if it already exists. + /// + public bool OverwriteDestination { get; set; } + + /// + /// If zipping an entire folder without exclusion patterns, whether to include the folder in the archive. + /// + public bool IncludeBaseDirectory { get; set; } + + /// + /// An item group of regular expressions for content to exclude from the archive. + /// + public ITaskItem[] ExcludePatterns { get; set; } + + public override bool Execute() + { + try + { + if (File.Exists(DestinationArchive)) + { + if (OverwriteDestination == true) + { + Log.LogMessage(MessageImportance.Low, "{0} already existed, deleting before zipping...", DestinationArchive); + File.Delete(DestinationArchive); + } + else + { + Log.LogWarning("'{0}' already exists. Did you forget to set '{1}' to true?", DestinationArchive, nameof(OverwriteDestination)); + } + } + + Log.LogMessage(MessageImportance.High, "Compressing {0} into {1}...", SourceDirectory, DestinationArchive); + if (!Directory.Exists(Path.GetDirectoryName(DestinationArchive))) + Directory.CreateDirectory(Path.GetDirectoryName(DestinationArchive)); + + if (ExcludePatterns == null) + { + ZipFile.CreateFromDirectory(SourceDirectory, DestinationArchive, CompressionLevel.Optimal, IncludeBaseDirectory); + } + else + { + // convert to regular expressions + Regex[] regexes = new Regex[ExcludePatterns.Length]; + for (int i = 0; i < ExcludePatterns.Length; ++i) + regexes[i] = new Regex(ExcludePatterns[i].ItemSpec, RegexOptions.IgnoreCase); + + using (FileStream writer = new FileStream(DestinationArchive, FileMode.CreateNew)) + { + using (ZipArchive zipFile = new ZipArchive(writer, ZipArchiveMode.Create)) + { + var files = Directory.GetFiles(SourceDirectory, "*", SearchOption.AllDirectories); + + foreach (var file in files) + { + // look for a match + bool foundMatch = false; + foreach (var regex in regexes) + { + if (regex.IsMatch(file)) + { + foundMatch = true; + break; + } + } + + if (foundMatch) + { + Log.LogMessage(MessageImportance.Low, "Excluding {0} from archive.", file); + continue; + } + + var relativePath = MakeRelativePath(SourceDirectory, file); + zipFile.CreateEntryFromFile(file, relativePath, CompressionLevel.Optimal); + } + } + } + } + } + catch (Exception e) + { + // We have 2 log calls because we want a nice error message but we also want to capture the callstack in the log. + Log.LogError("An exception has occurred while trying to compress '{0}' into '{1}'.", SourceDirectory, DestinationArchive); + Log.LogMessage(MessageImportance.Low, e.ToString()); + return false; + } + + return true; + } + + private string MakeRelativePath(string root, string subdirectory) + { + if (!subdirectory.StartsWith(root)) + throw new Exception(string.Format("'{0}' is not a subdirectory of '{1}'.", subdirectory, root)); + + // returned string should not start with a directory separator + int chop = root.Length; + if (subdirectory[chop] == Path.DirectorySeparatorChar) + ++chop; + + return subdirectory.Substring(chop); + } + } +} diff --git a/src/core-sdk-tasks/core-sdk-tasks.csproj b/src/core-sdk-tasks/core-sdk-tasks.csproj new file mode 100644 index 000000000..5ad9818bc --- /dev/null +++ b/src/core-sdk-tasks/core-sdk-tasks.csproj @@ -0,0 +1,25 @@ + + + $(CoreSdkTargetFramework);net472 + $(CoreSdkTargetFramework) + true + Microsoft.DotNet.Cli.Build + true + true + + + + + + + + + + + + + + + + + diff --git a/src/finalizer/CMakeLists.txt b/src/finalizer/CMakeLists.txt new file mode 100644 index 000000000..63baa941a --- /dev/null +++ b/src/finalizer/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.20) + +# Create project named finalizer, this will generate Finalizer.vcxproj +project(Finalizer) + +set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:wmainCRTStartup") + +# The WiX SDK is extracted from a NuGet package using an SDK .csproj (finalizer-build) that copies the "lib" and "inc" folders to a stable location. +# The defines below (values in ${}) are set in the finalizer.nativeproj. +# See: https://github.com/dotnet/arcade/tree/main/src/Microsoft.DotNet.CMake.Sdk#common-items +# Note: The directory paths use forward slashes because backslashes are escape characters. +include_directories(${ArtifactsDir}WixSdk/inc) +include_directories(${ArtifactsDir}obj) +link_directories(${ArtifactsDir}WixSdk/lib/${Platform}) + +add_compile_options(/MT) + +# Microsoft.Security.SystemsADM.10086 +add_compile_options($<$:/W3>) +add_compile_options($<$:/WX>) +add_compile_options($<$:/we4018>) # 'expression' : signed/unsigned mismatch +add_compile_options($<$:/we4055>) # 'conversion' : from data pointer 'type1' to function pointer 'type2' +add_compile_options($<$:/we4146>) # unary minus operator applied to unsigned type, result still unsigned +add_compile_options($<$:/we4242>) # 'identifier' : conversion from 'type1' to 'type2', possible loss of data +add_compile_options($<$:/we4244>) # 'conversion' conversion from 'type1' to 'type2', possible loss of data +add_compile_options($<$:/we4267>) # 'var' : conversion from 'size_t' to 'type', possible loss of data +add_compile_options($<$:/we4302>) # 'conversion' : truncation from 'type 1' to 'type 2' +add_compile_options($<$:/we4308>) # negative integral constant converted to unsigned type +add_compile_options($<$:/we4509>) # nonstandard extension used: 'function' uses SEH and 'object' has destructor +add_compile_options($<$:/we4510>) # 'class' : default constructor could not be generated +add_compile_options($<$:/we4532>) # 'continue' : jump out of __finally/finally block has undefined behavior during termination handling +add_compile_options($<$:/we4533>) # initialization of 'variable' is skipped by 'instruction' +add_compile_options($<$:/we4610>) # object 'class' can never be instantiated - user-defined constructor required +add_compile_options($<$:/we4611>) # interaction between 'function' and C++ object destruction is non-portable +add_compile_options($<$:/we4700>) # uninitialized local variable 'name' used +add_compile_options($<$:/we4701>) # Potentially uninitialized local variable 'name' used +add_compile_options($<$:/we4703>) # Potentially uninitialized local pointer variable 'name' used +add_compile_options($<$:/we4789>) # destination of memory copy is too small +add_compile_options($<$:/we4995>) # 'function': name was marked as #pragma deprecated +add_compile_options($<$:/we4996>) # 'function': was declared deprecated also 'std::' + +add_executable(Finalizer + finalizer.cpp + native.rc +) + +# These are normally part of a .vcxproj in Visual Studio, but appears to be missing when CMAKE generates a .vcxproj for arm64. +target_link_libraries(Finalizer shell32.lib) +target_link_libraries(Finalizer advapi32.lib) +target_link_libraries(Finalizer version.lib) +target_link_libraries(Finalizer msi.lib) +target_link_libraries(Finalizer shlwapi.lib) + +# Add WiX libraries +target_link_libraries(Finalizer wcautil.lib) +target_link_libraries(Finalizer dutil.lib) + +install(TARGETS Finalizer) diff --git a/src/finalizer/Directory.Build.props b/src/finalizer/Directory.Build.props new file mode 100644 index 000000000..f3ea03cfd --- /dev/null +++ b/src/finalizer/Directory.Build.props @@ -0,0 +1,9 @@ + + + + true + + + + + diff --git a/src/finalizer/README.md b/src/finalizer/README.md new file mode 100644 index 000000000..e4b4ffa79 --- /dev/null +++ b/src/finalizer/README.md @@ -0,0 +1,22 @@ +# Manually Testing Finalizer Changes + +The finalizer can be partially tested in isolation by creating the necessary registry keys +an installation would create. These can be easily configured on a VM or using Windows Sandbox. Otherwise, a full build of the installer is required along with the modified finalizer that results in a longer inner loop. + +The finalizer will use the default hive, e.g., when compiled for x64, the finalizer will default to using the 64-bit registry hive. + +### SDK Installation key + +The installation key resides under the 32-bit hive, regardless of the OS architecture. + +`REG ADD HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x64\sdk /v 6.0.107 /t REG_DWORD /d 1 /reg:32` + +### Workload Pack Records + +Records related to workload packs are created by the pack installers. Testing changes to the removal process requires the packs to be installed since the finalizer relies on using specific data such as the workload pack installer's product code to remove the installation. + +### Workload Records + +A workload record entry can be created using the following command. The key resides in either hive depending on the SDK installation. Records are associated with an SDK feature band, e.g., 6.0.100. + +`REG ADD HKLM\SOFTWARE\Microsoft\dotnet\InstalledWorkloads\Standalone\x64\6.0.100\wasm-tools` diff --git a/src/finalizer/finalizer-build.csproj b/src/finalizer/finalizer-build.csproj new file mode 100644 index 000000000..c30a53bea --- /dev/null +++ b/src/finalizer/finalizer-build.csproj @@ -0,0 +1,39 @@ + + + + $(CoreSdkTargetFramework) + true + false + false + $(ArtifactsObjDir)sdk_version.h + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/finalizer/finalizer.cpp b/src/finalizer/finalizer.cpp new file mode 100644 index 000000000..03a1213ba --- /dev/null +++ b/src/finalizer/finalizer.cpp @@ -0,0 +1,605 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "precomp.h" + +extern "C" HRESULT Initialize(int argc, wchar_t* argv[]) +{ + HRESULT hr = S_OK; + + // We're not going to do any clever parsing. This is intended to be called from + // the standalone bundle only and there will only be a fixed set of parameters: + // 1. The path of the log file, created by the bundle. + // 2. The full SDK version, e.g. 6.0.105 or 6.0.398-preview.19 + // 3. Target platform to search under the registry key to locate installed SDKs. + if (4 != argc) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_COMMAND_LINE); + } + + LogInitialize(::GetModuleHandleW(NULL)); + +#ifdef _DEBUG + LogSetLevel(REPORT_DEBUG, FALSE); +#else + LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed +#endif + + hr = LogOpen(NULL, argv[1], NULL, NULL, FALSE, TRUE, NULL); + ExitOnFailure(hr, "Failed to create log file."); + + hr = RegInitialize(); + ExitOnFailure(hr, "Failed to initialize the registry."); + + hr = WiuInitialize(); + ExitOnFailure(hr, "Failed to initialize Windows Installer."); + +LExit: + return hr; +} + +extern "C" HRESULT StrTrimBackslash(LPWSTR* ppwz, LPCWSTR wzSource) +{ + HRESULT hr = S_OK; + LPWSTR sczResult = NULL; + + int i = lstrlenW(wzSource); + + if (0 < i) + { + for (i = i - 1; i > 0; --i) + { + if (L'\\' != wzSource[i]) + { + break; + } + } + + ++i; + } + + hr = StrAllocString(&sczResult, wzSource, i); + ExitOnFailure(hr, "Failed to copy result string"); + + // Output result + *ppwz = sczResult; + sczResult = NULL; + +LExit: + ReleaseStr(sczResult); + + return hr; +} + +extern "C" HRESULT DeleteWorkloadRecords(LPWSTR sczSdkFeatureBandVersion, LPWSTR sczArchitecture) +{ + HRESULT hr = S_OK; + LPWSTR sczKeyName = NULL; + LPWSTR pszName = NULL; + LPWSTR sczSubKey = NULL; + HKEY hkWorkloadRecordsKey = NULL; + HKEY hkCurrentKey = NULL; + DWORD dwIndex = 0; + DWORD dwType = 0; + DWORD_PTR cbKeyName = 0; + DWORD cbSubKeys = 0; + DWORD cbValues = 0; + BOOL bDeleteKey = FALSE; + + hr = StrAllocFormatted(&sczKeyName, L"SOFTWARE\\Microsoft\\dotnet\\InstalledWorkloads\\Standalone\\%ls", sczArchitecture); + ExitOnFailure(hr, "Failed to allocate string for workload records registry path."); + + hr = RegOpen(HKEY_LOCAL_MACHINE, sczKeyName, KEY_READ | KEY_WRITE, &hkWorkloadRecordsKey); + + if (S_OK == hr) + { + // Delete the SDK feature band's workload records. + hr = RegDelete(hkWorkloadRecordsKey, sczSdkFeatureBandVersion, REG_KEY_DEFAULT, TRUE); + ExitOnFailure(hr, "Failed to delete workload records key under '%ls' for '%ls'.", sczKeyName, sczSdkFeatureBandVersion); + LogStringLine(REPORT_STANDARD, "Deleted workload records for '%ls'.", sczSdkFeatureBandVersion); + } + else if (E_FILENOTFOUND == hr) + { + // Ignore missing registry keys. + hr = S_OK; + } + ExitOnFailure(hr, "Failed to open workload records key: %ls.", sczKeyName); + + // Clean out empty registry keys by walking backwards. Eventually we'll hit HKLM\SOFTWARE\Microsoft and stop. + for (;;) + { + bDeleteKey = TRUE; + LogStringLine(REPORT_STANDARD, "Processing '%ls'.", sczKeyName); + hr = RegOpen(HKEY_LOCAL_MACHINE, sczKeyName, KEY_READ | KEY_WRITE, &hkCurrentKey); + + if (E_FILENOTFOUND != hr && S_OK != hr) + { + ExitOnFailure(hr, "Failed to open registry key: %ls", sczKeyName); + } + + if (S_OK == hr) + { + hr = RegQueryKey(hkCurrentKey, &cbSubKeys, &cbValues); + ExitOnFailure(hr, "Failed to query key info."); + + if (0 < cbSubKeys || 0 < cbValues) + { + // If the current key has any subkeys or values then we're done. + LogStringLine(REPORT_STANDARD, "Non-empty key found. '%ls' contains %d value(s) and %d subkey(s).", sczKeyName, cbValues, cbSubKeys); + break; + } + + LogStringLine(REPORT_STANDARD, "'%ls' is empty and can be deleted.", sczKeyName); + ReleaseRegKey(hkCurrentKey); + } + else + { + // We want to continue traversing up the registry, but we can't delete a non-existing key. + LogStringLine(REPORT_STANDARD, "'%ls' does not exist, continuing.", sczKeyName); + bDeleteKey = FALSE; + } + + // Move up one level and delete the current key. For example, if we looked at SOFTWARE\Microsoft\dotnet\InstalledWorkloads\Standalone\x64, we'll + // delete the x64 subkey. + hr = StrSize(sczKeyName, &cbKeyName); + ExitOnFailure(hr, "Failed to get size of key name."); + + // Need to remove trailing backslash otherwise PathFile returns an empty string. + hr = StrTrimBackslash(&sczKeyName, sczKeyName); + ExitOnFailure(hr, "Failed to remove backslash."); + + hr = StrAllocString(&sczSubKey, PathFile(sczKeyName), 0); + ExitOnFailure(hr, "Failed to allocate string for subkey."); + + hr = PathGetParentPath(sczKeyName, &sczKeyName); + ExitOnFailure(hr, "Failed to get parent path of registry key."); + + if (bDeleteKey) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, sczKeyName, KEY_READ | KEY_WRITE, &hkCurrentKey); + ExitOnFailure(hr, "Failed to open registry key: %ls.", sczKeyName); + + hr = RegDelete(hkCurrentKey, sczSubKey, REG_KEY_DEFAULT, FALSE); + ExitOnFailure(hr, "Failed to delete registry key '%ls' under '%ls'", sczSubKey, sczKeyName); + + ReleaseRegKey(hkCurrentKey); + } + } + +LExit: + ReleaseStr(sczKeyName); + ReleaseStr(pszName); + ReleaseStr(sczSubKey); + ReleaseRegKey(hkCurrentKey); + ReleaseRegKey(hkWorkloadRecordsKey); + return hr; +} + +extern "C" HRESULT RemoveDependent(LPWSTR sczDependent, BOOL * pbRestartRequired) +{ + HRESULT hr = S_OK; + HKEY hkInstallerDependenciesKey = NULL; + HKEY hkProviderKey = NULL; + HKEY hkDependentsKey = NULL; + LPWSTR sczProviderKey = NULL; + LPWSTR sczDependentsKey = NULL; + LPWSTR sczProductId = NULL; + LPWSTR sczProductName = NULL; + DWORD cSubKeys = 0; + DWORD dwExitCode = 0; + WIU_RESTART restart = WIU_RESTART_NONE; + + // Optional workloads are always per-machine installs, so we don't need to check HKCU. + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Classes\\Installer\\Dependencies", KEY_READ, &hkInstallerDependenciesKey); + if (E_FILENOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "Installer dependencies key does not exit."); + hr = S_OK; + goto LExit; + } + ExitOnFailure(hr, "Failed to read installer dependencies key."); + + // This has to be an exhaustive search as we're not looking for a specific provider key, but for a specific dependent + // that could be registered against any provider key. + for (DWORD dwIndex = 0;; ++dwIndex) + { + // Get the next provider key name + hr = RegKeyEnum(hkInstallerDependenciesKey, dwIndex, &sczProviderKey); + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + + ExitOnFailure(hr, "Failed to enumerate installer dependency provider keys."); + LogStringLine(REPORT_STANDARD, "Processing provider key: %ls", sczProviderKey); + + hr = RegOpen(hkInstallerDependenciesKey, sczProviderKey, KEY_READ, &hkProviderKey); + ExitOnFailure(hr, "Unable to open provider key."); + + // Open the dependents key with write permissions so we can modify it if it matches + // the target dependent value. + hr = RegOpen(hkProviderKey, L"Dependents", KEY_READ | KEY_WRITE, &hkDependentsKey); + if (E_FILENOTFOUND == hr) + { + // Providers can sometimes become orphaned during uninstalls. If there's no Dependents subkey, we just + // release the handle and continue to the next provider key. + hr = S_OK; + ReleaseRegKey(hkProviderKey); + + continue; + } + + ExitOnFailure(hr, "Unable to open dependents key."); + + // Enumerate over all the dependent keys + for (DWORD dwDependentsKeyIndex = 0;; ++dwDependentsKeyIndex) + { + hr = RegKeyEnum(hkDependentsKey, dwDependentsKeyIndex, &sczDependentsKey); + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + + ExitOnFailure(hr, "Failed to read provider's dependent key."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczDependentsKey, -1, sczDependent, -1)) + { + LogStringLine(REPORT_STANDARD, " Dependent match found: %ls", sczDependentsKey); + + hr = RegDelete(hkDependentsKey, sczDependent, REG_KEY_DEFAULT, TRUE); + ExitOnFailure(hr, "Failed to delete dependent \"%ls\"", sczDependent); + LogStringLine(REPORT_STANDARD, " Dependent deleted"); + // Reset the index since we're deleting keys while enumerating + dwDependentsKeyIndex = dwDependentsKeyIndex > 1 ? dwDependentsKeyIndex-- : 0; + + // Check if there are any subkeys remaining under the dependents key. If not, we + // can uninstall the MSI. We'll recheck the key again in case the MSI fails to clean up the + // provider key to make sure we don't have orphaned keys. + hr = RegQueryKey(hkDependentsKey, &cSubKeys, NULL); + ExitOnFailure(hr, "Failed to query dependents key."); + + LogStringLine(REPORT_STANDARD, " Remaining dependents: %i", cSubKeys); + + if (0 == cSubKeys) + { + // This was the final dependent, so now we can remove the installation if the provider wasn't corrupted and + // still contains the product ID. + hr = RegReadString(hkProviderKey, NULL, &sczProductId); + + if (E_FILENOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, " No product ID found, provider key: %ls", sczProviderKey); + hr = S_OK; + break; + } + else + { + ExitOnFailure(hr, "Failed to read product ID."); + } + + // Let's make sure the product is actually installed. The provider key for an MSI typically + // stores the ProductCode, DisplayName, and Version, but by calling into MsiGetProductInfo, + // we're doing an implicit detect and getting a property back. + hr = WiuGetProductInfo(sczProductId, L"ProductName", &sczProductName); + if (SUCCEEDED(hr)) + { + // The provider key *should* have the ProductName and ProductVersion properties, but since + // we know it's installed, we just query the installer service. + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + hr = WiuConfigureProductEx(sczProductId, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, L"MSIFASTINSTALL=7 IGNOREDEPENDENCIES=ALL REBOOT=ReallySuppress", &restart); + LogStringLine(REPORT_STANDARD, " Uninstall of \"%ls\" (%ls%) exited with 0x%.8x", sczProductName, sczProductId, hr); + + // Flag any reboot since we need to return that to the bundle. + if (WIU_RESTART_INITIATED == restart || WIU_RESTART_REQUIRED == restart) + { + LogStringLine(REPORT_STANDARD, " Reboot requested, deferring."); + *pbRestartRequired = TRUE; + } + + // Reset potential failures so we can continue to remove as many dependents as possible. + hr = S_OK; + } + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) + { + // Possibly a corrupted provider key that wasn't cleaned up. We'll just ignore it. + LogStringLine(REPORT_STANDARD, " Product is not installed, ProductCode:%ls, result: 0x%.8x", sczProductId, hr); + hr = S_OK; + } + } + } + } + + ReleaseRegKey(hkDependentsKey); + ReleaseRegKey(hkProviderKey); + } + +LExit: + ReleaseStr(sczProductName); + ReleaseStr(sczProductId); + ReleaseStr(sczProviderKey); + ReleaseStr(sczDependentsKey); + ReleaseRegKey(hkDependentsKey); + ReleaseRegKey(hkProviderKey); + ReleaseRegKey(hkInstallerDependenciesKey); + return hr; +} + +extern "C" HRESULT ParseSdkVersion(LPWSTR sczSdkVersion, LPWSTR * ppwzSdkFeatureBandVersion) +{ + HRESULT hr = S_OK; + UINT cVersionParts = 0; + UINT cSemanticParts = 0; + UINT cPrereleaseParts = 0; + DWORD cchPatch = 0; + LPWSTR* rgsczVersionParts = NULL; + LPWSTR* rgsczSemanticParts = NULL; + LPWSTR* rgsczPrereleaseParts = NULL; + LPWSTR sczPrereleaseLabel = NULL; + int iMajor = 0; + int iMinor = 0; + int iFeatureBand = 0; + int iPatch = 0; + + LogStringLine(REPORT_STANDARD, "Parsing SDK version: %ls", sczSdkVersion); + + // Split the version to separate potential prerelease labels from the core version + hr = StrSplitAllocArray(&rgsczSemanticParts, &cSemanticParts, sczSdkVersion, L"-"); + ExitOnFailure(hr, "Failed to split version."); + + if (2 == cSemanticParts) + { + LogStringLine(REPORT_STANDARD, "Semantic version component: %ls", rgsczSemanticParts[1]); + + hr = StrSplitAllocArray(&rgsczPrereleaseParts, &cPrereleaseParts, rgsczSemanticParts[1], L"."); + ExitOnFailure(hr, "Failed to split prerelease labels."); + + // SDK versions for CI/DEV builds map to pure feature band versions, e.g. 6.0.108-ci maps to 6.0.100. + if ((CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, rgsczPrereleaseParts[0], -1, L"dev", -1)) && + (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, rgsczPrereleaseParts[0], -1, L"ci", -1))) + { + if (1 <= cPrereleaseParts) + { + hr = StrAllocFormatted(&sczPrereleaseLabel, L"%ls.%ls", rgsczPrereleaseParts[0], rgsczPrereleaseParts[1]); + ExitOnFailure(hr, "Failed to allocate string for prerelease label."); + } + else + { + hr = StrAllocFormatted(&sczPrereleaseLabel, L"%ls", rgsczPrereleaseParts[0]); + ExitOnFailure(hr, "Failed to allocate string for prerelease label."); + } + + LogStringLine(REPORT_STANDARD, "Prerelease label: %ls", sczPrereleaseLabel); + } + } + + // Split the core version + hr = StrSplitAllocArray(&rgsczVersionParts, &cVersionParts, rgsczSemanticParts[0], L"."); + ExitOnFailure(hr, "Failed to split version."); + + // We only care about the major.minor.patch values + // to convert to a feature band. If we don't have at least + // all 3 parts, we'll ignore the value. + if (3 > cVersionParts) + { + ExitOnFailure(E_INVALIDARG, "Invalid SDK version: %ls %li", sczSdkVersion, cVersionParts); + } + + hr = StrStringToInt32(rgsczVersionParts[0], 0, &iMajor); + ExitOnFailure(hr, "Invalid major version."); + hr = StrStringToInt32(rgsczVersionParts[1], 0, &iMinor); + ExitOnFailure(hr, "Invalid minor version."); + + // If this is a valid SDK version the 'patch' should be a 3 digit field + // containing the feature band and patch level, e.g. 100 or 207. We + // can discard any prerelease labels from the semantic version. + hr = StrStringToInt32(rgsczVersionParts[2], 0, &iPatch); + ExitOnFailure(hr, "Invalid patch version."); + + if (100 > iPatch) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid SDK feature band and patch level."); + } + + iFeatureBand = iPatch - (iPatch % 100); + + if (NULL == sczPrereleaseLabel) + { + hr = StrAllocFormatted(ppwzSdkFeatureBandVersion, L"%li.%li.%li", iMajor, iMinor, iFeatureBand); + ExitOnFailure(hr, "Failed to allocate string for SDK feature band version."); + } + else + { + hr = StrAllocFormatted(ppwzSdkFeatureBandVersion, L"%li.%li.%li-%ls", iMajor, iMinor, iFeatureBand, sczPrereleaseLabel); + ExitOnFailure(hr, "Failed to allocate string for SDK feature band version."); + } + + LogStringLine(REPORT_STANDARD, "SDK feature band version: %ls", *ppwzSdkFeatureBandVersion); + +LExit: + ReleaseStrArray(rgsczVersionParts, cVersionParts); + ReleaseStrArray(rgsczSemanticParts, cSemanticParts); + ReleaseStrArray(rgsczPrereleaseParts, cPrereleaseParts); + ReleaseStr(sczPrereleaseLabel); + return hr; +} + +extern "C" HRESULT DetectSdk(LPWSTR sczSdkFeatureBandVersion, LPWSTR sczArchitecture, BOOL * pbInstalled) +{ + HRESULT hr = S_OK; + HKEY hkInstalledSdkVersionsKey = NULL; + LPWSTR sczInstalledSdkVersionsKeyName = NULL; + LPWSTR sczSdkVersion = NULL; + LPWSTR sczInstalledFeatureBand = NULL; + DWORD dwSdkVersionValueType = 0; + + LogStringLine(REPORT_STANDARD, "Detecting installed SDK versions for %ls", sczSdkFeatureBandVersion); + + // Scan the registry to see if any SDK matching the feature band we're trying to + // clean up is still installed. All the installation keys reside in the 32-bit hive. + hr = StrAllocFormatted(&sczInstalledSdkVersionsKeyName, L"SOFTWARE\\WOW6432Node\\dotnet\\Setup\\InstalledVersions\\%ls\\sdk", sczArchitecture); + ExitOnFailure(hr, "Failed to allocate string for installed SDK versions key name."); + + LogStringLine(REPORT_STANDARD, "Scanning %ls", sczInstalledSdkVersionsKeyName); + + hr = RegOpen(HKEY_LOCAL_MACHINE, sczInstalledSdkVersionsKeyName, KEY_READ, &hkInstalledSdkVersionsKey); + + // When the last SDK is removed the registry key should no longer exist so we can just exit + if (E_FILENOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "Registry key not found: %ls.", sczInstalledSdkVersionsKeyName); + hr = S_OK; + *pbInstalled = FALSE; + goto LExit; + } + + ExitOnFailure(hr, "Failed to open registry key: %ls.", sczInstalledSdkVersionsKeyName); + + for (DWORD dwSdkVersionsValueIndex = 0;; ++dwSdkVersionsValueIndex) + { + hr = RegValueEnum(hkInstalledSdkVersionsKey, dwSdkVersionsValueIndex, &sczSdkVersion, &dwSdkVersionValueType); + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + + ExitOnFailure(hr, "Failed to read SDK version values from registry."); + + hr = ParseSdkVersion(sczSdkVersion, &sczInstalledFeatureBand); + ExitOnFailure(hr, "Failed to parse %ls", sczSdkVersion); + + LogStringLine(REPORT_STANDARD, "SDK version detected: %ls, mapping to %ls.", sczSdkVersion, sczInstalledFeatureBand); + + // Bail out on the first match. + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczInstalledFeatureBand, -1, sczSdkFeatureBandVersion, -1)) + { + *pbInstalled = TRUE; + break; + } + } + +LExit: + ReleaseRegKey(hkInstalledSdkVersionsKey); + ReleaseStr(sczInstalledSdkVersionsKeyName); + ReleaseStr(sczSdkVersion); + ReleaseStr(sczInstalledFeatureBand); + return hr; +} + +void RemoveInstallStateFile(LPWSTR sczSdkFeatureBandVersion, LPWSTR sczPlatform) +{ + HRESULT hr = S_OK; + LPWSTR sczProgramData = NULL; + LPWSTR sczInstallStatePath = NULL; + LPWSTR sczPath = NULL; + + hr = ShelGetFolder(&sczProgramData, CSIDL_COMMON_APPDATA); + ExitOnFailure(hr, "Failed to get shell folder."); + + hr = PathConcat(sczProgramData, L"dotnet", &sczInstallStatePath); + ExitOnFailure(hr, "Failed to concat dotnet to install state path."); + + hr = PathConcat(sczInstallStatePath, L"workloads", &sczInstallStatePath); + ExitOnFailure(hr, "Failed to concat workloads to install state path."); + + hr = PathConcat(sczInstallStatePath, sczPlatform, &sczInstallStatePath); + ExitOnFailure(hr, "Failed to concat platform (%ls) to install state path.", sczPlatform); + + hr = PathConcat(sczInstallStatePath, sczSdkFeatureBandVersion, &sczInstallStatePath); + ExitOnFailure(hr, "Failed to concat feature band (%ls) to install state path.", sczSdkFeatureBandVersion); + + hr = PathConcat(sczInstallStatePath, L"installstate", &sczInstallStatePath); + ExitOnFailure(hr, "Failed to concat installstate to install state path."); + + hr = PathConcat(sczInstallStatePath, L"default.json", &sczInstallStatePath); + ExitOnFailure(hr, "Failed to concat default.json to install state path."); + + if (FileExistsEx(sczInstallStatePath, NULL)) + { + LogStringLine(REPORT_STANDARD, "Deleting install state file: %ls", sczInstallStatePath); + hr = FileEnsureDelete(sczInstallStatePath); + ExitOnFailure(hr, "Failed to delete install state file: %ls", sczInstallStatePath); + + hr = PathGetParentPath(sczInstallStatePath, &sczPath); + ExitOnFailure(hr, "Failed to get parent path of install state file."); + + LogStringLine(REPORT_STANDARD, "Cleaning up empty workload folders."); + DirDeleteEmptyDirectoriesToRoot(sczPath, 0); + } + else + { + LogStringLine(REPORT_STANDARD, "Install state file does not exist: %ls", sczInstallStatePath); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczInstallStatePath) + ReleaseStr(sczProgramData); +} + +int wmain(int argc, wchar_t* argv[]) +{ + HRESULT hr = S_OK; + DWORD dwExitCode = 0; + LPWSTR sczDependent = NULL; + LPWSTR sczFeatureBandVersion = NULL; + LPWSTR sczPlatform = NULL; + BOOL bRestartRequired = FALSE; + BOOL bSdkFeatureBandInstalled = FALSE; + int iMajor = 0; + int iMinor = 0; + int iFeatureBand = 0; + + hr = ::Initialize(argc, argv); + ExitOnFailure(hr, "Failed to initialize."); + + hr = StrAllocString(&sczPlatform, argv[3], 0); + ExitOnFailure(hr, "Failed to copy platform argument."); + + // Convert the full SDK version to a feature band version + hr = ParseSdkVersion(argv[2], &sczFeatureBandVersion); + ExitOnFailure(hr, "Failed to parse version, %ls.", argv[2]); + + // Create the dependent value, e.g., Microsoft.NET.Sdk,6.0.300,arm64 + hr = StrAllocFormatted(&sczDependent, L"Microsoft.NET.Sdk,%ls,%ls", sczFeatureBandVersion, sczPlatform); + ExitOnFailure(hr, "Failed to create dependent."); + LogStringLine(REPORT_STANDARD, "Setting target dependent to %ls.", sczDependent); + + hr = ::DetectSdk(sczFeatureBandVersion, sczPlatform, &bSdkFeatureBandInstalled); + ExitOnFailure(hr, "Failed to detect installed SDKs."); + + // If the feature band is still present, do not remove workloads. + if (bSdkFeatureBandInstalled) + { + LogStringLine(REPORT_STANDARD, "Detected SDK with feature band %ls.", sczFeatureBandVersion); + goto LExit; + } + + hr = ::RemoveDependent(sczDependent, &bRestartRequired); + ExitOnFailure(hr, "Failed to remove dependent \"%ls\".", sczDependent); + + hr = ::DeleteWorkloadRecords(sczFeatureBandVersion, sczPlatform); + ExitOnFailure(hr, "Failed to remove workload records."); + + if (bRestartRequired) + { + dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; + } + + RemoveInstallStateFile(sczFeatureBandVersion, sczPlatform); + +LExit: + ReleaseStr(sczDependent); + ReleaseStr(sczFeatureBandVersion); + ReleaseStr(sczPlatform); + LogUninitialize(TRUE); + RegUninitialize(); + WiuUninitialize(); + return FAILED(hr) ? (int)hr : (int)dwExitCode; +} diff --git a/src/finalizer/finalizer.nativeproj b/src/finalizer/finalizer.nativeproj new file mode 100644 index 000000000..1d6b4c8a5 --- /dev/null +++ b/src/finalizer/finalizer.nativeproj @@ -0,0 +1,17 @@ + + + + + $([System.String]::Copy($(ArtifactsDir)).Replace('\','/')) + CMakeLists.txt + + + + + + + + + + + diff --git a/src/finalizer/native.rc b/src/finalizer/native.rc new file mode 100644 index 000000000..bea8f0b52 Binary files /dev/null and b/src/finalizer/native.rc differ diff --git a/src/finalizer/precomp.h b/src/finalizer/precomp.h new file mode 100644 index 000000000..97bc60b13 --- /dev/null +++ b/src/finalizer/precomp.h @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Configure some logging parameters for WiX +#define ExitTrace LogErrorString +#define ExitTrace1 LogErrorString +#define ExitTrace2 LogErrorString +#define ExitTrace3 LogErrorString + +// Includes from WiX SDK +#include "dutil.h" +#include "regutil.h" +#include "logutil.h" +#include "pathutil.h" +#include "strutil.h" +#include "wiutil.h" +#include "dirutil.h" +#include "fileutil.h" +#include "shelutil.h" + diff --git a/src/redist/packaging/deb/dotnet-debian_config.json b/src/redist/packaging/deb/dotnet-debian_config.json new file mode 100644 index 000000000..58d9111fa --- /dev/null +++ b/src/redist/packaging/deb/dotnet-debian_config.json @@ -0,0 +1,39 @@ +{ + "maintainer_name":"Microsoft", + "maintainer_email": "dotnetcore@microsoft.com", + + "package_name": "dotnet-sdk-%SDK_NUGET_VERSION%", + "install_root": "/usr/share/dotnet", + + "short_description": "%CLI_SDK_BRAND_NAME%", + "long_description": ".NET is a development platform that you can use to build command-line applications, microservices and modern websites. It is open source, cross-platform and is supported by Microsoft. We hope you enjoy using it! If you do, please consider joining the active community of developers that are contributing to the project on GitHub (https://github.com/dotnet/core). We happily accept issues and PRs.", + "homepage": "https://github.com/dotnet/core", + + "release": { + "package_version":"0.0.0.0", + "package_revision":"1", + "urgency" : "low", + "changelog_message" : "Bootstrap loop package" + }, + + "control": { + "priority":"standard", + "section":"devel", + "architecture":"any" + }, + + "copyright": ".NET Foundation and contributors", + "license": { + "type": "MIT", + "full_text": "The MIT License (MIT)\nCopyright (c) .NET Foundation and Contributors\n\nAll rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." + }, + + "debian_dependencies": { + "%SHARED_FRAMEWORK_DEBIAN_PACKAGE_NAME%": { "package_version": "%SHARED_FRAMEWORK_DEBIAN_PACKAGE_VERSION%" }%SHARED_FRAMEWORK_DEBIAN_PACKAGE_ADDITIONAL_DEPENDENCY%, + "%NETCOREAPP_TARGETING_PACK_DEBIAN_PACKAGE_NAME%": { "package_version": "%NETCOREAPP_TARGETING_PACK_DEBIAN_PACKAGE_VERSION%" }, + "%NETCORE_APPHOST_PACK_DEBIAN_PACKAGE_NAME%": { "package_version": "%SHARED_FRAMEWORK_DEBIAN_PACKAGE_VERSION%" }, + "%NETSTANDARD_TARGETING_PACK_DEBIAN_PACKAGE_NAME%": { "package_version": "%NET_STANDARD_DEBIAN_PACKAGE_VERSION%" }, + "%ASPNETCORE_SHAREDFX_DEBIAN_PACKAGE_NAME%": { "package_version": "%ASPNETCORE_SHAREDFX_DEBIAN_PACKAGE_VERSION%" }, + "%ASPNET_TARGETING_PACK_DEBIAN_PACKAGE_NAME%": { "package_version": "%ASPNET_TARGETING_PACK_DEBIAN_PACKAGE_VERSION%" } + } +} diff --git a/src/redist/packaging/deb/postinst b/src/redist/packaging/deb/postinst new file mode 100644 index 000000000..696ec8acc --- /dev/null +++ b/src/redist/packaging/deb/postinst @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +first_run() { + /usr/share/dotnet/dotnet exec /usr/share/dotnet/sdk/%SDK_VERSION%/dotnet.dll internal-reportinstallsuccess "debianpackage" > /dev/null 2>&1 || true +} + +INSTALL_TEMP_HOME=$(mktemp -d) # mktemp should set 700 perm automatically +HOME=$INSTALL_TEMP_HOME first_run diff --git a/src/redist/packaging/osx/Distribution-Template b/src/redist/packaging/osx/Distribution-Template new file mode 100644 index 000000000..62bea3d7d --- /dev/null +++ b/src/redist/packaging/osx/Distribution-Template @@ -0,0 +1,49 @@ + + {CLISdkBrandName} ({arch}) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {NetCoreAppTargetingPackComponentId}.pkg + {NetStandardTargetingPackComponentId}.pkg + {NetCoreAppHostPackComponentId}.pkg + {SharedFxComponentId}.pkg + {HostFxrComponentId}.pkg + {SharedHostComponentId}.pkg + {CLISdkComponentId}.pkg + diff --git a/src/redist/packaging/osx/Distribution-Template-x64 b/src/redist/packaging/osx/Distribution-Template-x64 new file mode 100644 index 000000000..c702c5c72 --- /dev/null +++ b/src/redist/packaging/osx/Distribution-Template-x64 @@ -0,0 +1,100 @@ + + {CLISdkBrandName} ({arch}) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {NetCoreAppTargetingPackComponentId}.pkg + {NetStandardTargetingPackComponentId}.pkg + {NetCoreAppHostPackComponentId}.pkg + {SharedFxComponentId}.pkg + {HostFxrComponentId}.pkg + {SharedHostComponentId}.pkg + {CLISdkComponentId}.pkg + + diff --git a/src/redist/packaging/osx/resources/conclusion.html.lci b/src/redist/packaging/osx/resources/conclusion.html.lci new file mode 100644 index 000000000..0192efed0 --- /dev/null +++ b/src/redist/packaging/osx/resources/conclusion.html.lci @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/cs.lproj/conclusion.html b/src/redist/packaging/osx/resources/cs.lproj/conclusion.html new file mode 100644 index 000000000..79216d0de --- /dev/null +++ b/src/redist/packaging/osx/resources/cs.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

Nainstalovaly se následující položky: +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Tento produkt shromažďuje data o využití +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/cs.lproj/welcome.html b/src/redist/packaging/osx/resources/cs.lproj/welcome.html new file mode 100644 index 000000000..478ab71c1 --- /dev/null +++ b/src/redist/packaging/osx/resources/cs.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET je vývojová platforma, kterou můžete použít k vytváření aplikací příkazového řádku, mikroslužeb a moderních webů. Je open source, multiplatformní a podporuje ho Microsoft. Doufáme, že se vám bude líbit!

+
+ +
+
+

Poznámka k instalaci

+

Během procesu instalace se spustí příkaz, který zlepší rychlost obnovení projektu a povolí offline přístup. Dokončení bude trvat až minutu.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/de.lproj/conclusion.html b/src/redist/packaging/osx/resources/de.lproj/conclusion.html new file mode 100644 index 000000000..88d0fcbfe --- /dev/null +++ b/src/redist/packaging/osx/resources/de.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

Folgendes wurde installiert +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Dieses Produkt sammelt Verbrauchsdaten +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/de.lproj/welcome.html b/src/redist/packaging/osx/resources/de.lproj/welcome.html new file mode 100644 index 000000000..f2244ce01 --- /dev/null +++ b/src/redist/packaging/osx/resources/de.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET ist eine Entwicklungsplattform, die Sie zum Erstellen von Befehlszeilenanwendungen, Microservices und modernen Websites verwenden können. Sie ist Open Source und plattformübergreifend und wird von Microsoft unterstützt. Wir wünschen Ihnen viel Spaß damit!

+
+
+

Weitere Informationen zu .NET

+ +
+
+
+

Installationshinweis

+

Während des Installationsvorgangs wird ein Befehl ausgeführt, durch den die Geschwindigkeit der Projektwiederherstellung verbessert und der Offlinezugriff aktiviert wird. Der Vorgang dauert bis zu einer Minute.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/dotnetbackground.png b/src/redist/packaging/osx/resources/dotnetbackground.png new file mode 100644 index 000000000..16a6bf22b Binary files /dev/null and b/src/redist/packaging/osx/resources/dotnetbackground.png differ diff --git a/src/redist/packaging/osx/resources/en.lproj/conclusion.html b/src/redist/packaging/osx/resources/en.lproj/conclusion.html new file mode 100644 index 000000000..b95c78cfb --- /dev/null +++ b/src/redist/packaging/osx/resources/en.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

The following was installed +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

This product collects usage data +

+
+
+ + + diff --git a/src/redist/packaging/osx/resources/en.lproj/welcome.html b/src/redist/packaging/osx/resources/en.lproj/welcome.html new file mode 100644 index 000000000..477a68ebe --- /dev/null +++ b/src/redist/packaging/osx/resources/en.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET is a development platform that you can use to build command-line applications, microservices and modern websites. It is open source, cross-platform, and supported by Microsoft. We hope you enjoy it!

+
+
+

Learn more about .NET

+ +
+
+
+

Installation note

+

A command will be run during the install process that will improve project restore speed and enable offline access. It will take up to a minute to complete.

+
+ + diff --git a/src/redist/packaging/osx/resources/es.lproj/conclusion.html b/src/redist/packaging/osx/resources/es.lproj/conclusion.html new file mode 100644 index 000000000..08e4bc6f6 --- /dev/null +++ b/src/redist/packaging/osx/resources/es.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

Se ha instalado lo siguiente +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Este producto recopila datos de uso +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/es.lproj/welcome.html b/src/redist/packaging/osx/resources/es.lproj/welcome.html new file mode 100644 index 000000000..5d29bf216 --- /dev/null +++ b/src/redist/packaging/osx/resources/es.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET es una plataforma de desarrollo que puede utilizar para crear aplicaciones de línea de comandos, microservicios y sitios web modernos. Es de código abierto, multiplataforma y cuenta con el respaldo de Microsoft. ¡Esperamos que lo disfrute!

+
+ +
+
+

Nota de instalación

+

Durante el proceso de instalación se ejecutará un comando que mejorará la velocidad de restauración del proyecto y permitirá el acceso sin conexión. Tardará hasta un minuto en completarse.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/fr.lproj/conclusion.html b/src/redist/packaging/osx/resources/fr.lproj/conclusion.html new file mode 100644 index 000000000..52a3457c9 --- /dev/null +++ b/src/redist/packaging/osx/resources/fr.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

L’élément suivant a été installé +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Ce produit collecte des données d’utilisation +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/fr.lproj/welcome.html b/src/redist/packaging/osx/resources/fr.lproj/welcome.html new file mode 100644 index 000000000..d09490c7c --- /dev/null +++ b/src/redist/packaging/osx/resources/fr.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET est une plateforme de développement qui vous permet de générer des applications en ligne de commande, des microservices et des sites web modernes. Il s’agit d’un framework open source, multiplateforme et pris en charge par Microsoft. Nous espérons que vous l’apprécierez !

+
+ +
+
+

Note d’installation

+

Une commande va être exécutée pendant le processus d’installation, ce qui va améliorer la vitesse de restauration du projet et permettre l’accès hors connexion. L’opération va prendre environ une minute.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/it.lproj/conclusion.html b/src/redist/packaging/osx/resources/it.lproj/conclusion.html new file mode 100644 index 000000000..729ff5702 --- /dev/null +++ b/src/redist/packaging/osx/resources/it.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

È stato installato quanto segue +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Questo prodotto raccoglie i dati di utilizzo +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/it.lproj/welcome.html b/src/redist/packaging/osx/resources/it.lproj/welcome.html new file mode 100644 index 000000000..b846ed27d --- /dev/null +++ b/src/redist/packaging/osx/resources/it.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET è una piattaforma di sviluppo che è possibile usare per creare microservizi, siti Web moderni e applicazioni dalla riga di comando. È open source, multipiattaforma e supportata da Microsoft.

+
+ +
+
+

Nota sull'installazione

+

Durante il processo di installazione verrà eseguito un comando che migliorerà la velocità di ripristino del progetto e abiliterà l'accesso offline. Il completamento del comando richiederà un minuto.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/ja.lproj/conclusion.html b/src/redist/packaging/osx/resources/ja.lproj/conclusion.html new file mode 100644 index 000000000..1ab4a4027 --- /dev/null +++ b/src/redist/packaging/osx/resources/ja.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

以下がインストールされました +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

この製品は利用状況データを収集します +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/ja.lproj/welcome.html b/src/redist/packaging/osx/resources/ja.lproj/welcome.html new file mode 100644 index 000000000..d7b6d158c --- /dev/null +++ b/src/redist/packaging/osx/resources/ja.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.Net は、コマンドライン アプリケーション、マイクロサービス、および最新の Web サイトをビルドするために使用できる開発プラットフォームです。オープンソースのクロス プラットフォームで、Microsoft によってサポートされています。お楽しみいただければ幸いです。

+
+ +
+
+

インストール メモ

+

コマンドはインストール処理中に実行されるので、プロジェクトの復元速度が向上し、オフラインでアクセスできます。完了するまでに最大 1 分かかります。

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/ko.lproj/conclusion.html b/src/redist/packaging/osx/resources/ko.lproj/conclusion.html new file mode 100644 index 000000000..af7f152f0 --- /dev/null +++ b/src/redist/packaging/osx/resources/ko.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

다음이 설치되었습니다. +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

이 제품은 사용량 현황 데이터를 수집합니다. +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/ko.lproj/welcome.html b/src/redist/packaging/osx/resources/ko.lproj/welcome.html new file mode 100644 index 000000000..6fb40c480 --- /dev/null +++ b/src/redist/packaging/osx/resources/ko.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET은 명령줄 애플리케이션, 마이크로 서비스 및 최신 웹 사이트를 빌드하는 데 사용할 수 있는 개발 플랫폼입니다. Microsoft가 지원하는 플랫폼 간 오픈 소스입니다. 즐기시기 바랍니다!

+
+
+

.NET에 대한 자세한 정보

+ +
+
+
+

설치 정보

+

프로젝트 복원 속도를 향상하고 오프라인 액세스를 사용할 수 있도록 하는 설치 프로세스 중 명령이 실행됩니다. 완료하는 데 최대 1분이 걸립니다.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/pl.lproj/conclusion.html b/src/redist/packaging/osx/resources/pl.lproj/conclusion.html new file mode 100644 index 000000000..41d3e4bd3 --- /dev/null +++ b/src/redist/packaging/osx/resources/pl.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

Zainstalowano następujące elementy +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Ten produkt zbiera dane użycia +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/pl.lproj/welcome.html b/src/redist/packaging/osx/resources/pl.lproj/welcome.html new file mode 100644 index 000000000..a97d4e8dc --- /dev/null +++ b/src/redist/packaging/osx/resources/pl.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET to platforma programistyczna, za pomocą której można kompilować aplikacje poziomu wiersza polecenia, mikrousługi i nowoczesne witryny internetowe. Jest to rozwiązanie typu open source, międzyplatformowe i obsługiwane przez firmę Microsoft. Mamy nadzieję, że Ci się spodoba!

+
+ +
+
+

Uwaga dotycząca instalacji

+

W trakcie procesu instalacji zostanie uruchomione polecenie, które zwiększy szybkość przywracania projektu i umożliwi dostęp do trybu offline. Ukończenie tego procesu zajmie maksymalnie minutę.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/pt-br.lproj/conclusion.html b/src/redist/packaging/osx/resources/pt-br.lproj/conclusion.html new file mode 100644 index 000000000..bf8eef457 --- /dev/null +++ b/src/redist/packaging/osx/resources/pt-br.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

O seguinte foi instalado +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Esse produto coleta dados de uso +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/pt-br.lproj/welcome.html b/src/redist/packaging/osx/resources/pt-br.lproj/welcome.html new file mode 100644 index 000000000..457fb3a5f --- /dev/null +++ b/src/redist/packaging/osx/resources/pt-br.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

O .NET é uma plataforma de desenvolvimento que você pode usar para criar aplicativos de linha de comando, microsserviços e sites modernos. É um software livre, multi-plataforma e suportado pela Microsoft. Esperamos que você goste!

+
+ +
+
+

Nota de instalação

+

Um comando será executado durante o processo de instalação que melhorará a velocidade da restauração do projeto e permitirá o acesso offline. A conclusão levará até um minuto.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/ru.lproj/conclusion.html b/src/redist/packaging/osx/resources/ru.lproj/conclusion.html new file mode 100644 index 000000000..8aab312fb --- /dev/null +++ b/src/redist/packaging/osx/resources/ru.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

Следующее было установлено +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Этот продукт собирает данные об использовании. +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/ru.lproj/welcome.html b/src/redist/packaging/osx/resources/ru.lproj/welcome.html new file mode 100644 index 000000000..a72cdaf65 --- /dev/null +++ b/src/redist/packaging/osx/resources/ru.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET — это платформа разработки, которую можно использовать для создания приложений командной строки, микрослужб и современных веб-сайтов. Это кроссплатформенная система с открытым кодом, которая поддерживается корпорацией Майкрософт. Надеемся, что она вам понравится!

+
+ +
+
+

Примечание по установке

+

В процессе установки будет выполнена команда, которая увеличит скорость восстановления проекта и обеспечит автономный доступ. Выполнение займет до минуты.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/tr.lproj/conclusion.html b/src/redist/packaging/osx/resources/tr.lproj/conclusion.html new file mode 100644 index 000000000..a15637eb2 --- /dev/null +++ b/src/redist/packaging/osx/resources/tr.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

Aşağıdakiler yüklendi: +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

Bu ürün, kullanım verilerini toplar +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/tr.lproj/welcome.html b/src/redist/packaging/osx/resources/tr.lproj/welcome.html new file mode 100644 index 000000000..c29d4c7e6 --- /dev/null +++ b/src/redist/packaging/osx/resources/tr.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET, komut satırı uygulamaları, mikro hizmetler ve modern web siteleri oluşturmak için kullanabileceğiniz bir geliştirme platformudur. Açık kaynaktır, platformlar arası kullanılabilir ve Microsoft tarafından desteklenmektedir. Keyfini çıkarmanızı umuyoruz!

+
+
+

.NET hakkında daha fazla bilgi edinin

+ +
+
+
+

Yükleme notu

+

Yükleme işlemi sırasında proje geri yükleme hızını artıran ve çevrimdışı erişimi etkinleştiren bir komut çalıştırılır. Tamamlanması bir dakikanızı alır.

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/welcome.html.lci b/src/redist/packaging/osx/resources/welcome.html.lci new file mode 100644 index 000000000..13ba010ee --- /dev/null +++ b/src/redist/packaging/osx/resources/welcome.html.lci @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/zh-hans.lproj/conclusion.html b/src/redist/packaging/osx/resources/zh-hans.lproj/conclusion.html new file mode 100644 index 000000000..0dde9c060 --- /dev/null +++ b/src/redist/packaging/osx/resources/zh-hans.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

已安装以下项 +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

此产品会收集使用情况数据 +

+
+
+
+

资源

+ +
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/zh-hans.lproj/welcome.html b/src/redist/packaging/osx/resources/zh-hans.lproj/welcome.html new file mode 100644 index 000000000..8093c0a6d --- /dev/null +++ b/src/redist/packaging/osx/resources/zh-hans.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET 是一款开发平台,可用于生成命令行应用程序、微服务和新式网站。它是开放源代码、跨平台的,且受 Microsoft 支持。希望你喜欢它!

+
+
+

了解有关 .NET 的详细信息

+ +
+
+
+

安装说明

+

将在要提升项目还原速度并实现脱机访问的安装进程期间运行命令。此操作最多 1 分钟即可完成。

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/zh-hant.lproj/conclusion.html b/src/redist/packaging/osx/resources/zh-hant.lproj/conclusion.html new file mode 100644 index 000000000..ff397625b --- /dev/null +++ b/src/redist/packaging/osx/resources/zh-hant.lproj/conclusion.html @@ -0,0 +1,33 @@ + + + + + + +
+

已安裝下列項目 +

    +
  • .NET SDK {DOTNETSDKVERSION}
  • +
  • .NET Runtime {DOTNETRUNTIMEVERSION}
  • +
  • ASP.NET Core Runtime {ASPNETCOREVERSION}
  • +
+
+
+
+

此產品會收集使用方式資料 +

+
+
+ + + \ No newline at end of file diff --git a/src/redist/packaging/osx/resources/zh-hant.lproj/welcome.html b/src/redist/packaging/osx/resources/zh-hant.lproj/welcome.html new file mode 100644 index 000000000..08b5f92f5 --- /dev/null +++ b/src/redist/packaging/osx/resources/zh-hant.lproj/welcome.html @@ -0,0 +1,27 @@ + + + + + + +
+
+

.NET SDK

+

.NET 是開發平台,可用來建置命令列應用程式、微服務和新式網站。其為開放原始碼、跨平台且由 Microsoft 支援。希望您喜歡!

+
+
+

深入了解 .NET

+ +
+
+
+

安裝附註

+

安裝程序期間將會執行命令,加快專案還原速度並啟用離線存取。最多需要一分鐘的時間完成。

+
+ + \ No newline at end of file diff --git a/src/redist/packaging/osx/scripts/postinstall b/src/redist/packaging/osx/scripts/postinstall new file mode 100644 index 000000000..86710223d --- /dev/null +++ b/src/redist/packaging/osx/scripts/postinstall @@ -0,0 +1,18 @@ +#!/bin/sh +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +PACKAGE=$1 +INSTALL_DESTINATION=$2 +INSTALL_TEMP_HOME=/tmp/dotnet-installer + +first_run() { + $INSTALL_DESTINATION/dotnet exec $INSTALL_DESTINATION/sdk/%SDK_VERSION%/dotnet.dll internal-reportinstallsuccess "$1" > /dev/null 2>&1 || true +} + +INSTALL_TEMP_HOME=$(mktemp -d) # mktemp should set 700 perm automatically +HOME=$INSTALL_TEMP_HOME first_run + +exit 0 diff --git a/src/redist/packaging/rpm/dotnet-config.json b/src/redist/packaging/rpm/dotnet-config.json new file mode 100644 index 000000000..1a9d84999 --- /dev/null +++ b/src/redist/packaging/rpm/dotnet-config.json @@ -0,0 +1,53 @@ +{ + "maintainer_name": "Microsoft", + "maintainer_email": "dotnetcore@microsoft.com", + "vendor": ".NET Foundation", + "package_name": "%SDK_RPM_PACKAGE_NAME%", + "install_root": "/usr/share/dotnet", + "install_doc": "/usr/share/doc/%SDK_RPM_PACKAGE_NAME%/", + "short_description": "%CLI_SDK_BRAND_NAME% %SDK_NUGET_VERSION%", + "long_description": ".NET is a development platform that you can use to build command-line applications, microservices and modern websites. It is open source, cross-platform and is supported by Microsoft. We hope you enjoy using it! If you do, please consider joining the active community of developers that are contributing to the project on GitHub (https://github.com/dotnet/core). We happily accept issues and PRs.", + "homepage": "https://github.com/dotnet/core", + "release": { + "package_version": "1.0.0.0", + "package_revision": "1", + "changelog_message": "Bootstrap loop package" + }, + "control": { + "architecture": "%SDK_RPM_PACKAGE_ARCHITECTURE%" + }, + "copyright": ".NET Foundation and contributors", + "license": { + "type": "MIT", + "full_text": "The MIT License (MIT)\nCopyright (c) .NET Foundation and Contributors\n\nAll rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." + }, + "rpm_dependencies": [ + { + "package_name": "%SHARED_FRAMEWORK_RPM_PACKAGE_NAME%", + "package_version": "%SHARED_FRAMEWORK_RPM_PACKAGE_VERSION%" + }, + { + "package_name": "%NETCOREAPP_TARGETING_PACK_RPM_PACKAGE_NAME%", + "package_version": "%NETCOREAPP_TARGETING_PACK_RPM_PACKAGE_VERSION%" + }, + { + "package_name": "%NETCORE_APPHOST_PACK_RPM_PACKAGE_NAME%", + "package_version": "%SHARED_FRAMEWORK_RPM_PACKAGE_VERSION%" + }, +%SDK_RPM_NETSTANDARD_TARGETINGPACK_DEPENDENCY% + { + "package_name": "%ASPNETCORE_SHAREDFX_RPM_PACKAGE_NAME%", + "package_version": "%ASPNETCORE_SHAREDFX_RPM_PACKAGE_VERSION%" + }, + { + "package_name": "%ASPNET_TARGETING_PACK_RPM_PACKAGE_NAME%", + "package_version": "%ASPNET_TARGETING_PACK_RPM_PACKAGE_VERSION%" + } + ], + "directories": [ + "/usr/share/dotnet/sdk", + "/usr/share/doc/%SDK_RPM_PACKAGE_NAME%" + ], + "after_install_source": "scripts/after_install_host.sh", + "after_remove_source": "scripts/after_remove_host.sh" +} diff --git a/src/redist/packaging/rpm/scripts/after_install_host.sh b/src/redist/packaging/rpm/scripts/after_install_host.sh new file mode 100644 index 000000000..31b3f346e --- /dev/null +++ b/src/redist/packaging/rpm/scripts/after_install_host.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +first_run() { + /usr/share/dotnet/dotnet exec /usr/share/dotnet/sdk/%SDK_VERSION%/dotnet.dll internal-reportinstallsuccess "rpmpackage" > /dev/null 2>&1 || true +} + +INSTALL_TEMP_HOME=/tmp/dotnet-installer +[ -d $INSTALL_TEMP_HOME ] || mkdir $INSTALL_TEMP_HOME +HOME=$INSTALL_TEMP_HOME first_run diff --git a/src/redist/packaging/rpm/scripts/after_remove_host.sh b/src/redist/packaging/rpm/scripts/after_remove_host.sh new file mode 100644 index 000000000..bb0cdef0a --- /dev/null +++ b/src/redist/packaging/rpm/scripts/after_remove_host.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# diff --git a/src/redist/packaging/rpm/templates/changelog b/src/redist/packaging/rpm/templates/changelog new file mode 100644 index 000000000..e61dbc607 --- /dev/null +++ b/src/redist/packaging/rpm/templates/changelog @@ -0,0 +1,2 @@ +* {DATE} {MAINTAINER_NAME} <{MAINTAINER_EMAIL}> - {PACKAGE_VERSION}-{PACKAGE_REVISION} +- {CHANGELOG_MESSAGE} diff --git a/src/redist/packaging/rpm/templates/copyright b/src/redist/packaging/rpm/templates/copyright new file mode 100644 index 000000000..a336435d6 --- /dev/null +++ b/src/redist/packaging/rpm/templates/copyright @@ -0,0 +1,8 @@ +Comment: Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. + +Files: * +Copyright: {COPYRIGHT_TEXT} +License: {LICENSE_NAME} + +License: {LICENSE_NAME} + {LICENSE_TEXT} \ No newline at end of file diff --git a/src/redist/packaging/windows/Init.cmd b/src/redist/packaging/windows/Init.cmd new file mode 100644 index 000000000..cee1e8f1a --- /dev/null +++ b/src/redist/packaging/windows/Init.cmd @@ -0,0 +1,15 @@ +@echo off + +set DOTNET_MULTILEVEL_LOOKUP=0 +set PATH=%~dp0;%PATH% + +if not "%PROCESSOR_ARCHITECTURE%"=="x86" goto :SetDotnetRoot_Wow +if not "%PROCESSOR_ARCHITEW6432%"== "" goto :SetDotnetRoot_Wow + +:SetDotnetRoot +set DOTNET_ROOT=%~dp0 +goto :eof + +:SetDotnetRoot_Wow +set DOTNET_ROOT(x86)=%~dp0 +goto :eof diff --git a/src/redist/packaging/windows/LCID/1028/bundle.wxl b/src/redist/packaging/windows/LCID/1028/bundle.wxl new file mode 100644 index 000000000..44aef5032 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1028/bundle.wxl @@ -0,0 +1,84 @@ + + [WixBundleName] 安裝程式 + [BUNDLEMONIKER] + 確定要取消嗎? + 前一版 + 安裝說明 + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 在目錄中安裝、修復、解除安裝 + 或建立搭售方案的完整本機複本。預設為 '/install'。 + +/passive | /quiet - 顯示最少 UI 且不含提示,或者不顯示 UI,也 + 不顯示提示。預設會顯示 UI 和所有提示。 + +/norestart - 隱藏任何重新啟動嘗試。根據預設,UI 會在重新啟動之前提示。 +/log [\[]"log.txt"[\]] - 記錄至特定檔案。預設會在 %TEMP% 建立記錄檔。 + 關閉(&C) + 我同意授權條款及條件(&A) + 選項(&O) + 安裝(&I) + 關閉(&C) + 安裝進度 + 處理中: + 正在初始化... + 取消(&C) + 修改安裝 + 修復(&R) + 解除安裝(&U) + 關閉(&C) + 修復已成功完成 + 解除安裝已成功完成 + 設定成功 + 啟動(&L) + 您必須重新啟動電腦,以完成軟體的安裝。 + 重新啟動(&R) + 關閉(&C) + 設定失敗 + 設定失敗 + 解除安裝失敗 + 修復失敗 + 有一個或多個問題導致安裝程式失敗。請解決問題,然後重試一次安裝。如需詳細資訊,請參閱<a href="#">記錄檔</a>。 + 必須重新啟動電腦,才能完成軟體的復原。 + 重新啟動(&R) + 關閉(&C) + 使用中的檔案 + 以下應用程式正在使用需要進行更新的檔案: + 關閉應用程式並嘗試重新啟動(&A) + 不關閉應用程式,需要重新啟動(&D) + 確定(&O) + 取消(&C) + 安裝成功。 + +已安裝下列產品: + * .NET SDK [DOTNETSDKVERSION] + * .NET Runtime [DOTNETRUNTIMEVERSION] + * ASP.NET Core Runtime [ASPNETCOREVERSION] + * .NET Windows Desktop Runtime [WINFORMSANDWPFVERSION] + +此產品會收集使用方式資料 + * 如需詳細資訊並退出,請前往 https://aka.ms/dotnet-cli-telemetry + +資源 + * 如需 .NET 文件,請前往 https://aka.ms/dotnet-docs + * 如需 SDK 文件,請前往 https://aka.ms/dotnet-sdk-docs + * 如需版本資訊,請前往 https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + * 如需教學課程,請前往 https://aka.ms/dotnet-tutorials + .NET SDK + + .NET SDK 可用於建置、執行及測試 .NET 應用程式。您可以選擇多種語言、編輯器以及開發人員工具,並可利用程式庫的大型生態系統,來建置 Web、行動裝置、桌面、遊戲及 IoT 的應用程式。希望您會喜歡! + 深入了解 .NET + 資源 + <A HREF="https://aka.ms/dotnet-docs">.NET 文件</A> + <A HREF="https://aka.ms/dotnet-cli-docs">SDK 文件</A> + <A HREF="https://aka.ms/dev-privacy">隱私權聲明</A> + <A HREF="https://aka.ms/dotnet-license-windows">.NET 的授權資訊</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">遙測集合與退出功能</A> + 安裝附註 + 安裝程序期間將會執行命令,加快專案還原速度並啟用離線存取。最多需要一分鐘的時間完成。 + + 若計劃要搭配 Visual Studio 使用 .NET [VERSIONMAJOR].[VERSIONMINOR],需要 Visual Studio 2022 [MINIMUMVSVERSION] 或更新版本。<A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">深入了解</A>. + + 按一下 “安裝” 即表示您同意下列條款: + x64 SDK 安裝的安裝路徑: "[DOTNETHOME_X64]" 不能與 x86 SDK 安裝的路徑相同: "[DOTNETHOME_X86]" + ARM64 SDK 安裝的安裝路徑: "[DOTNETHOME_ARM64]" 不能與 x86 SDK 安裝的路徑相同: "[DOTNETHOME_X86]" + ARM64 SDK 安裝的安裝路徑: "[DOTNETHOME_ARM64]" 不能與 x64 SDK 安裝的路徑相同: "[DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1029/bundle.wxl b/src/redist/packaging/windows/LCID/1029/bundle.wxl new file mode 100644 index 000000000..9503bd625 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1029/bundle.wxl @@ -0,0 +1,84 @@ + + Instalační program pro [WixBundleName] + [BUNDLEMONIKER] + Opravdu chcete akci zrušit? + Předchozí verze + Nápověda nastavení + /install | /repair | /uninstall | /layout [\[]"directory"[\]] – nainstaluje, opraví, odinstaluje + nebo vytvoří úplnou místní kopii sady prostředků v adresáři. /install je výchozí hodnota. + +/passive | /quiet – zobrazí minimální uživatelské rozhraní bez výzev nebo nezobrazí žádné uživatelské rozhraní a + žádné výzvy. Ve výchozím nastavení se zobrazí uživatelské rozhraní a všechny výzvy. + +/norestart – potlačí všechny pokusy o restartování. Ve výchozím nastavení uživatelské rozhraní zobrazí před restartováním výzvu. +/log [\[]"log.txt"[\]] – zaznamená do konkrétního souboru. Ve výchozím nastavení se v %TEMP% vytvoří soubor protokolu. + &Zavřít + Souhl&asím s licenčními podmínkami + M&ožnosti + &Instalovat + &Zavřít + Průběh instalace + Probíhá zpracování: + Inicializace... + &Storno + Změnit instalaci + Op&ravit + O&dinstalovat + &Zavřít + Oprava byla úspěšně dokončena + Odinstalace se úspěšně dokončila + Instalace byla úspěšná. + &Spustit + Aby bylo možné dokončit instalaci softwaru, je nutné restartovat počítač. + &Restartovat + &Zavřít + Instalace se nepovedla + Instalace se nepovedla + Odinstalace se nepovedla + Oprava se nepovedla + Nejméně jeden problém způsobil selhání instalace. Opravte prosím tyto problémy a zkuste instalaci zopakovat. Další informace najdete v <a href="#">log file</a>. + Pro dokončení vrácení změn tohoto softwaru je potřeba restartovat počítač. + &Restartovat + &Zavřít + Soubory jsou používány + Následující aplikace používají soubory, které je potřeba aktualizovat: + Zavřete &aplikace a zkuste je restartovat. + A&plikace nezavírejte. Bude potřeba provést restart. + &OK + &Zrušit + Instalace byla úspěšná. + +Byly nainstalovány následující produkty: + • .NET SDK [DOTNETSDKVERSION] + • .NET Runtime [DOTNETRUNTIMEVERSION] + • ASP.NET Core Runtime [ASPNETCOREVERSION] + • .NET Windows Desktop Runtime [WINFORMSANDWPFVERSION] + +Tento produkt shromažďuje data o využití + • Další informace a odhlášení https://aka.ms/dotnet-cli-telemetry + +Prostředky + • Dokumentace .NET https://aka.ms/dotnet-docs + • Dokumentace SDK https://aka.ms/dotnet-sdk-docs + • Poznámky k verzi https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Kurzy https://aka.ms/dotnet-tutorials + Sada .NET SDK + + Sada .NET SDK slouží k sestavování, spouštění a testování aplikací .NET. Můžete si vybrat z několika jazyků, editorů a vývojářských nástrojů a využít rozsáhlého ekosystému knihoven k vytváření aplikací pro web, mobilní zařízení, stolní počítače, hry a IoT. Doufáme, že se vám bude líbit! + Další informace o .NET + Prostředky + <A HREF="https://aka.ms/dotnet-docs">Dokumentace k architektuře .NET</A> + <A HREF="https://aka.ms/dotnet-cli-docs">Dokumentace k sadě SDK</A> + <A HREF="https://aka.ms/dev-privacy">Prohlášení o zásadách ochrany osobních údajů</A> + <A HREF="https://aka.ms/dotnet-license-windows">Informace o licencování pro .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Kolekce telemetrie a výslovný nesouhlas</A> + Poznámka k instalaci + Během procesu instalace se spustí příkaz, který zlepší rychlost obnovení projektu a povolí offline přístup. Akce se dokončí přibližně za minutu. + + Pokud plánujete používat .NET [VERSIONMAJOR].[VERSIONMINOR] s Visual Studio, vyžaduje se Visual Studio 2022 [MINIMUMVSVERSION] nebo novější. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Další informace</A>. + + Kliknutím na Nainstalovat vyjadřujete souhlas s následujícími podmínkami: + Instalační cesta pro instalace sady x64 SDK[ DOTNETHOME_X64] nemůže být stejná jako u instalací sady x86 SDK: [DOTNETHOME_X86]. + Instalační cesta pro instalace sady ARM64 SDK[ DOTNETHOME_ARM64] nemůže být stejná jako u instalací sady x86 SDK: [DOTNETHOME_X86] + Instalační cesta pro instalace sady ARM64 SDK[ DOTNETHOME_ARM64] nemůže být stejná jako u instalací sady x64 SDK: [DOTNETHOME_X64] + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1031/bundle.wxl b/src/redist/packaging/windows/LCID/1031/bundle.wxl new file mode 100644 index 000000000..0a0b289c9 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1031/bundle.wxl @@ -0,0 +1,84 @@ + + [WixBundleName]-Installationsprogramm + [BUNDLEMONIKER] + Möchten Sie den Vorgang wirklich abbrechen? + Vorherige Version + Setup-Hilfe + /install | /repair | /uninstall | /layout [\[]"directory"[\]] – installiert, repariert, deinstalliert + oder erstellt eine vollständige lokale Kopie des Bundles im Verzeichnis. „/install“ ist die Standardeinstellung. + +/passive | /quiet – zeigt eine minimale Benutzeroberfläche ohne Eingabeaufforderungen oder keine Benutzeroberfläche und + keine Eingabeaufforderungen an. Standardmäßig werden die Benutzeroberfläche und alle Eingabeaufforderungen angezeigt. + +/norestart – unterdrückt alle Neustartversuche. Standardmäßig fordert die Benutzeroberfläche zum Bestätigen eines Neustarts auf. +/log [\[]"log.txt"[\]] – protokolliert in eine bestimmte Datei. Standardmäßig wird die Protokolldatei in %TEMP% erstellt. + S&chließen + Ich &stimme den Bedingungen des Lizenzvertrags zu + &Optionen + &Installieren + S&chließen + Setupstatus + Wird verarbeitet: + Wird initialisiert... + &Abbrechen + Setup ändern + &Reparieren + &Deinstallieren + S&chließen + Reparatur erfolgreich abgeschlossen + Deinstallation erfolgreich abgeschlossen + Setup wurde erfolgreich abgeschlossen + &Starten + Sie müssen den Computer neu starten, um die Installation der Software abzuschließen. + &Neustart + S&chließen + Setupfehler + Fehler beim Setup + Fehler bei der Deinstallation + Fehler bei der Reparatur + Setup ist aufgrund eines oder mehrerer Probleme fehlgeschlagen. Beheben Sie die Probleme, und führen Sie Setup erneut aus. Weitere Informationen finden Sie in der <a href="#">Protokolldatei</a>. + Sie müssen den Computer neu starten, um das Zurücksetzen der Software abzuschließen. + &Neustart + S&chließen + Dateien in Verwendung + Die folgenden Anwendungen verwenden Dateien, die aktualisiert werden müssen: + Schließen Sie die &Anwendungen, und versuchen Sie sie erneut zu starten. + &Anwendungen nicht schließen. Ein Neustart ist erforderlich. + &OK + &Abbrechen + Die Installation war erfolgreich. + +Die folgenden Produkte wurden unter installiert: + • .NET-SDK [DOTNETSDKVERSION] + • .NET-Runtime [DOTNETRUNTIMEVERSION] + • ASP.NET Core-Runtime [ASPNETCOREVERSION] + • .NET Windows Desktop-Runtime [WINFORMSANDWPFVERSION] + +Dieses Produkt sammelt Verbrauchsdaten. + • Weitere Informationen und Deaktivieren https://aka.ms/dotnet-cli-telemetry + +Ressourcen + • .NET-Dokumentation: https://aka.ms/dotnet-docs + • SDK-Dokumentation: https://aka.ms/dotnet-sdk-docs + • Versionshinweise: https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Tutorials: https://aka.ms/dotnet-tutorials + .NET-SDK + + Das .NET-SDK wird zum Erstellen, Ausführen und Testen von .NET-Anwendungen verwendet. Sie können aus mehreren Sprachen, Editoren und Entwicklertools auswählen und ein großes Ökosystem von Bibliotheken nutzen, um Apps für das Web, mobile Geräte, Desktops, Gaming und IoT zu entwickeln. Wir wünschen Ihnen viel Spaß damit! + Weitere Informationen zu .NET + Ressourcen + <A HREF="https://aka.ms/dotnet-docs">.NET-Dokumentation</A> + <A HREF="https://aka.ms/dotnet-cli-docs">SDK-Dokumentation</A> + <A HREF="https://aka.ms/dev-privacy">Datenschutzerklärung</A> + <A HREF="https://aka.ms/dotnet-license-windows">Lizenzierungsinformationen für .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Sammlung von Telemetriedaten und Deaktivieren der Telemetriefunktion</A> + Installationshinweis + Während des Installationsvorgangs wird ein Befehl ausgeführt, durch den die Geschwindigkeit der Projektwiederherstellung verbessert und der Offlinezugriff aktiviert wird. Der Vorgang dauert bis zu einer Minute. + + Wenn Sie beabsichtigen, .NET [VERSIONMAJOR].[VERSIONMINOR] mit Visual Studio zu verwenden, ist Visual Studio 2022 [MINIMUMVSVERSION] oder höher erforderlich. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Weitere Informationen</A>. + + Durch Klicken auf „Installieren2 stimmen Sie den nachstehenden Bedingungen zu: + Der Installationspfad „[DOTNETHOME_X64]“ für x64 SDK-Installationen kann nicht derselbe sein wie für x86 SDK-Installationen: „[DOTNETHOME_X86]“ + Der Installationspfad „[DOTNETHOME_ARM64]“ für ARM64 SDK-Installationen kann nicht derselbe sein wie für x86 SDK-Installationen: „[DOTNETHOME_X86]“ + Der Installationspfad „[DOTNETHOME_ARM64]“ für ARM64 SDK-Installationen kann nicht derselbe sein wie für x64 SDK-Installationen: „[DOTNETHOME_X64]“ + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1033/bundle.wxl b/src/redist/packaging/windows/LCID/1033/bundle.wxl new file mode 100644 index 000000000..8c028c1ee --- /dev/null +++ b/src/redist/packaging/windows/LCID/1033/bundle.wxl @@ -0,0 +1,84 @@ + + [WixBundleName] Installer + [BUNDLEMONIKER] + Are you sure you want to cancel? + Previous version + Setup Help + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - installs, repairs, uninstalls + or creates a complete local copy of the bundle in directory. '/install' is the default. + +/passive | /quiet - displays minimal UI with no prompts or displays no UI and + no prompts. By default, UI and all prompts are displayed. + +/norestart - suppress any attempts to restart. By default, UI will prompt before restart. +/log [\[]"log.txt"[\]] - logs to a specific file. By default, a log file is created in %TEMP%. + &Close + I &agree to the license terms and conditions + &Options + &Install + &Close + Setup Progress + Processing: + Initializing... + &Cancel + Modify Setup + &Repair + &Uninstall + &Close + Repair Successfully Completed + Uninstall Successfully Completed + Setup Successful + &Launch + You must restart your computer to complete the installation of the software. + &Restart + &Close + Setup Failed + Setup Failed + Uninstall Failed + Repair Failed + One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information, see the <a href="#">log file</a>. + You must restart your computer to complete the rollback of the software. + &Restart + &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel + The installation was successful. + +The following products were installed: + • .NET SDK [DOTNETSDKVERSION] + • .NET Runtime [DOTNETRUNTIMEVERSION] + • ASP.NET Core Runtime [ASPNETCOREVERSION] + • .NET Windows Desktop Runtime [WINFORMSANDWPFVERSION] + +This product collects usage data + • More information and opt-out https://aka.ms/dotnet-cli-telemetry + +Resources + • .NET Documentation https://aka.ms/dotnet-docs + • SDK Documentation https://aka.ms/dotnet-sdk-docs + • Release Notes https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Tutorials https://aka.ms/dotnet-tutorials + .NET SDK + + The .NET SDK is used to build, run, and test .NET applications. You can choose from multiple languages, editors, and developer tools, and take advantage of a large ecosystem of libraries to build apps for web, mobile, desktop, gaming, and IoT. We hope you enjoy it! + Learn more about .NET + Resources + <A HREF="https://aka.ms/dotnet-docs">.NET Documentation</A> + <A HREF="https://aka.ms/dotnet-cli-docs">SDK Documentation</A> + <A HREF="https://aka.ms/dev-privacy">Privacy Statement</A> + <A HREF="https://aka.ms/dotnet-license-windows">Licensing Information for .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Telemetry collection and opt-out</A> + Installation note + A command will be run during the install process that will improve project restore speed and enable offline access. It will take up to a minute to complete. + + If you plan to use .NET [VERSIONMAJOR].[VERSIONMINOR] with Visual Studio, Visual Studio 2022 [MINIMUMVSVERSION] or newer is required. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Learn more</A>. + + By clicking Install, you agree to the following terms: + The installation path for x64 SDK installations: "[DOTNETHOME_X64]" cannot be the same as for x86 SDK installations: "[DOTNETHOME_X86]" + The installation path for ARM64 SDK installations: "[DOTNETHOME_ARM64]" cannot be the same as for x86 SDK installations: "[DOTNETHOME_X86]" + The installation path for ARM64 SDK installations: "[DOTNETHOME_ARM64]" cannot be the same as for x64 SDK installations: "[DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1036/bundle.wxl b/src/redist/packaging/windows/LCID/1036/bundle.wxl new file mode 100644 index 000000000..0341ae549 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1036/bundle.wxl @@ -0,0 +1,84 @@ + + Programme d’installation de [WixBundleName] + [BUNDLEMONIKER] + Voulez-vous vraiment annuler ? + Version précédente + Aide du programme d'installation + /install | /repair | /uninstall | /layout [\[]"répertoire"[\]] - installe, répare, désinstalle + ou crée une copie locale complète de l’offre groupée dans le répertoire. "/install" est l’option par défaut. + +/passive | /quiet - affiche une interface utilisateur minimale, sans invite, ou n’affiche + ni interface utilisateur, ni invite. Par défaut, l’interface utilisateur et toutes les invites sont affichées. + +/norestart - supprime toutes les tentatives de redémarrage. Par défaut, l’interface utilisateur affiche une invite avant le redémarrage. +/log [\[]"log.txt"[\]] - journalise dans un fichier spécifique. Par défaut, un fichier journal est créé dans %TEMP%. + &Fermer + J'&accepte les conditions générales de la licence + &Options + &Installer + &Fermer + Avancement de l'installation + En cours : + Initialisation... + &Annuler + Modifier l'installation + &Réparer + &Désinstaller + &Fermer + Réparation terminée avec succès + Désinstallation terminée avec succès + Opération réussie + &Démarrer + Vous devez redémarrer l'ordinateur pour achever l'installation du logiciel. + &Redémarrer + &Fermer + Échec de l'installation + Échec de l'installation + Échec de la désinstallation + Échec de la réparation + Le programme d’installation a échoué en raison d’un ou de plusieurs problèmes. Veuillez corriger ces problèmes, puis relancez le programme d’installation. Pour en savoir plus, consultez le <a href="#">fichier journal</a>. + Vous devez redémarrer votre ordinateur pour terminer l'opération de restauration du logiciel. + &Redémarrer + &Fermer + Fichiers en cours d'utilisation + Les applications suivantes utilisent des fichiers nécessitant une mise à jour : + &Fermer les applications essayer de les ouvrir de nouveau. + &Ne pas fermer les applications. Un redémarrage sera nécessaire. + &OK + &Annuler + L’installation a réussi. + +Les éléments suivants ont été installés : + • Kit SDK .NET [DOTNETSDKVERSION] + • Runtime .NET [DOTNETRUNTIMEVERSION] + • Runtime ASP.NET Core [ASPNETCOREVERSION] + • Runtime .NET Windows Desktop [WINFORMSANDWPFVERSION] + +Ce produit collecte des données d’utilisation + • Plus informations et refus d’adhésion sur https://aka.ms/dotnet-cli-telemetry + +Ressources + • Documentation .NET sur https://aka.ms/dotnet-docs + • Documentation de kit SDK sur https://aka.ms/dotnet-sdk-docs + • Notes de publication sur https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Tutoriels sur https://aka.ms/dotnet-tutorials + Kit de développement logiciel (SDK) .NET + + Le Kit de développement logiciel (SDK) .NET permet de générer, d’exécuter et de tester des applications .NET. Vous pouvez choisir parmi plusieurs langages, éditeurs et outils de développement. De plus, vous pouvez bénéficier d’un vaste écosystème de bibliothèques afin de générer des applications pour le web, les appareils mobiles, les ordinateurs de bureau, les jeux et l’IoT. Nous espérons que vous l’apprécierez ! + En savoir plus sur .NET + Ressources + <A HREF="https://aka.ms/dotnet-docs">Documentation .NET</A> + <A HREF="https://aka.ms/dotnet-cli-docs">Documentation du kit SDK</A> + <A HREF="https://aka.ms/dev-privacy">Déclaration de confidentialité</A> + <A HREF="https://aka.ms/dotnet-license-windows">Informations de licence pour .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Collecte et désactivation de la télémétrie</A> + Note d’installation + Une commande va être exécutée pendant le processus d'installation, ce qui va améliorer la vitesse de restauration du projet et permettre l'accès hors connexion. L'opération va prendre environ une minute. + + Si vous envisagez d’utiliser .NET [VERSIONMAJOR].[VERSIONMINOR] avec Visual Studio, Visual Studio 2022 [MINIMUMVSVERSION] ou une version ultérieure est nécessaire. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">En savoir plus</A>. + + En cliquant sur Installer, vous acceptez les conditions suivantes : + Le chemin d’installation des installations du Kit de développement logiciel (SDK) x64 : « [DOTNETHOME_X64] » ne peut pas être identique à celui des installations du Kit de développement logiciel (SDK) x86 : « [DOTNETHOME_X86] » + Le chemin d’installation des installations du Kit de développement logiciel (SDK) ARM64 : "[DOTNETHOME_ARM64]" ne peut pas être identique à celui des installations du Kit de développement logiciel (SDK) x86 : "[DOTNETHOME_X86]" + Le chemin d’installation des installations du Kit de développement logiciel (SDK) ARM64 : « [DOTNETHOME_ARM64] » ne peut pas être identique à celui des installations du Kit de développement logiciel (SDK) x64 : « [DOTNETHOME_X64] » + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1040/bundle.wxl b/src/redist/packaging/windows/LCID/1040/bundle.wxl new file mode 100644 index 000000000..3843fc36b --- /dev/null +++ b/src/redist/packaging/windows/LCID/1040/bundle.wxl @@ -0,0 +1,84 @@ + + Programma di installazione di [WixBundleName] + [BUNDLEMONIKER] + Annullare? + Versione precedente + Guida all'installazione + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - installa, ripara, disinstalla o + crea una copia locale completa del bundle nella directory. L'opzione predefinita è '/install'. + +/passive | /quiet - visualizza un'interfaccia utente minima senza prompt oppure non visualizza alcuna interfaccia utente + né prompt. Per impostazione predefinita, viene visualizzata l'intera interfaccia utente e tutti i prompt. + +/norestart - annulla qualsiasi tentativo di riavvio. Per impostazione predefinita, l'interfaccia utente visualizza una richiesta prima del riavvio. +/log [\[]"log.txt"[\]] - registra il log in un file specifico. Per impostazione predefinita, viene creato un file di log in %TEMP%. + &Chiudi + &Accetto i termini e le condizioni di licenza + &Opzioni + &Installa + &Chiudi + Stato installazione + Elaborazione di: + Inizializzazione in corso... + Ann&ulla + Modifica installazione + &Ripristina + &Disinstalla + &Chiudi + La riparazione è stata completata + La disinstallazione è stata completata + Installazione completata + &Avvia + Per completare l'installazione del software, è necessario riavviare il computer. + &Riavvia + &Chiudi + Installazione non riuscita + L'installazione non è riuscita + Disinstallazione non riuscita + La riparazione non è riuscita + Installazione non riuscita a causa di uno o più problemi. Risolvere i problemi e ritentare l'installazione. Per ulteriori informazioni, vedere <a href="#">log file</a>. + È necessario riavviare il computer per completare il rollback del software. + &Riavvia + &Chiudi + File in uso + Le applicazioni seguenti usano file che necessitano di aggiornamento: + Chiudere le &applicazioni e provare a riavviarle. + &Non chiudere le applicazioni; sarà necessario riavviare il sistema + &OK + &Annulla + Installazione completata. + +Sono stati installati i prodotti seguenti: + • .NET SDK [DOTNETSDKVERSION] + • Runtime di .NET [DOTNETRUNTIMEVERSION] + • Runtime di ASP.NET Core [ASPNETCOREVERSION] + • Runtime di Windows Desktop .NET [WINFORMSANDWPFVERSION] + +Questo prodotto raccoglie i dati di utilizzo + • Altre informazioni e rifiuto esplicito https://aka.ms/dotnet-cli-telemetry + +Risorse + • Documentazione .NET https://aka.ms/dotnet-docs + • Documentazione SDK https://aka.ms/dotnet-sdk-docs + • Note sulla versione https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Tutorial https://aka.ms/dotnet-tutorials + .NET SDK + + .NET SDK consente di creare, eseguire e testare applicazioni .NET. È possibile scegliere tra più linguaggi, editor e strumenti di sviluppo e sfruttare un vasto ecosistema di librerie per creare app per Web, dispositivi mobili, desktop, giochi e IoT. + Altre informazioni su .NET + Risorse + <A HREF="https://aka.ms/dotnet-docs">Documentazione di .NET</A> + <A HREF="https://aka.ms/dotnet-cli-docs">Documentazione dell'SDK</A> + <A HREF="https://aka.ms/dev-privacy">Informativa sulla privacy</A> + <A HREF="https://aka.ms/dotnet-license-windows">Informazioni sulla licenza per .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Raccolta e rifiuto esplicito dei dati di telemetria</A> + Nota sull'installazione + Durante il processo di installazione verrà eseguito un comando che migliorerà la velocità di ripristino del progetto e abiliterà l'accesso offline. Il completamento del comando richiederà un minuto. + + Se si prevede di usare .NET [VERSIONMAJOR]. [VERSIONMINOR] con Visual Studio, è necessario Visual Studio 2022 [MINIMUMVSVERSION] o versione successiva. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Altre informazioni</A>. + + Facendo clic su Installa, si accettano le condizioni seguenti: + Percorso di installazione per le installazioni x64 SDK: "[DOTNETHOME_X64]" non può essere uguale a quello delle installazioni di x86 SDK: "[DOTNETHOME_X86]" + Percorso di installazione per le installazioni di ARM64 SDK: "[DOTNETHOME_ARM64]" non può essere uguale a quello delle installazioni di x86 SDK: "[DOTNETHOME_X86]" + Percorso di installazione per le installazioni di ARM64 SDK: "[DOTNETHOME_ARM64]" non può essere uguale a quello delle installazioni x64 SDK: "[DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1041/bundle.wxl b/src/redist/packaging/windows/LCID/1041/bundle.wxl new file mode 100644 index 000000000..b3cd2ef40 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1041/bundle.wxl @@ -0,0 +1,84 @@ + + [WixBundleName] インストーラー + [BUNDLEMONIKER] + 取り消しますか? + 以前のバージョン + セットアップのヘルプ + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - バンドルの完全なローカル コピーに対する + ディレクトリへのインストール、修復、ディレクトリからのアンインストール、または作成を行います。既定は '/install' です。 + +/passive | /quiet - 最小限の UI だけを表示しプロンプトは表示しない、または UI もプロンプトも + 表示しません。既定では UI とすべてのプロンプトが表示されます。 + +/norestart - 再起動を抑制します。既定では再起動前に UI が確認ダイアログを表示します。 +/log [\[]"log.txt"[\]] - 特定のファイルにログを記録します。既定ではログ ファイルは %TEMP% に作成されます。 + 閉じる(&C) + ライセンス条項および使用条件に同意する(&A) + オプション(&O) + インストール(&I) + 閉じる(&C) + セットアップの進行状況 + 処理中: + 初期化しています... + キャンセル(&C) + セットアップの変更 + 修復(&R) + アンインストール(&U) + 閉じる(&C) + 修復が正常に完了しました + アンインストールが正常に完了しました + セットアップ完了 + 起動(&L) + ソフトウェアのインストールを完了するには、コンピューターを再起動する必要があります。 + 再起動(&R) + 閉じる(&C) + セットアップに失敗しました + セットアップに失敗しました + アンインストールに失敗しました + 修復に失敗しました + 1 つまたは複数の問題により、セットアップが失敗しました。問題を解決してからセットアップを再試行してください。詳細については、<a href="#">ログ ファイル</a>を参照してください。 + ソフトウェアのロールバックを完了するには、コンピューターを再起動する必要があります。 + 再起動(&R) + 閉じる(&C) + ファイルが使用中 + 次のアプリケーションは、更新の必要があるファイルを使用しています: + アプリケーションを閉じて再起動を試みる。(&A) + アプリケーションを終了させない (コンピューターの再起動が必要になります)(&D) + OK(&O) + キャンセル(&C) + インストールが成功しました。 + +以下の製品がインストールされました + • .NET SDK [DOTNETSDKVERSION] + • .NET Runtime [DOTNETRUNTIMEVERSION] + • ASP.NET Core Runtime [ASPNETCOREVERSION] + • .NET Windows Desktop Runtime [WINFORMSANDWPFVERSION] + +この製品は使用状況データを収集します + • 詳細およびオプトアウト https://aka.ms/dotnet-cli-telemetry + +リソース + • .NET ドキュメント https://aka.ms/dotnet-docs + • SDK ドキュメント https://aka.ms/dotnet-sdk-docs + • リリース ノート https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • チュートリアル https://aka.ms/dotnet-tutorials + .NET SDK + + .Net SDK は、.NET アプリケーションをビルド、実行、テストするために使用されます。複数の言語、エディター、開発者ツールから選択し、ライブラリの大規模なエコシステムを利用して、Web、モバイル、デスクトップ、ゲーム、IoT 用のアプリを作成できます。お楽しみいただければ幸いです。 + .Net の詳細情報 + リソース + <A HREF="https://aka.ms/dotnet-docs">.NET ドキュメント</A> + <A HREF="https://aka.ms/dotnet-cli-docs">SDK ドキュメント</A> + <A HREF="https://aka.ms/dev-privacy">プライバシーに関する声明</A> + <A HREF="https://aka.ms/dotnet-license-windows">.NET のライセンス情報</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">テレメトリ コレクションおよびオプトアウト</A> + インストール メモ + コマンドはインストール処理中に実行されるので、プロジェクトの復元速度が向上し、オフラインでアクセスできます。完了するまでに最大 1 分かかります。 + + .NET [VERSIONMAJOR] を使用する予定の場合。Visual Studio、Visual Studio 2022 [MINIMUMVSVERSION] 以降の [VERSIONMINOR] が必要です。<A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">詳細情報</A>. + + インストール をクリックすると、次の条項に同意したものと見なされます: + x64 SDK インストールのインストール パス: "[DOTNETHOME_X64]" を x86 SDK インストールの場合と同じにすることはできません: "[DOTNETHOME_X86]" + ARM64 SDK インストールのインストール パス: "[DOTNETHOME_ARM64]" を x86 SDK インストールの場合と同じにすることはできません: "[DOTNETHOME_X86]" + ARM64 SDK インストールのインストール パス: "[DOTNETHOME_ARM64]" を x64 SDK インストールの場合と同じにすることはできません: "[DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1042/bundle.wxl b/src/redist/packaging/windows/LCID/1042/bundle.wxl new file mode 100644 index 000000000..ee9c53407 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1042/bundle.wxl @@ -0,0 +1,84 @@ + + [WixBundleName] 설치 관리자 + [BUNDLEMONIKER] + 취소하시겠습니까? + 이전 버전 + 설치 도움말 + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 디렉터리에 번들의 전체 로컬 복사본을 설치, 복구, 제거 + 또는 작성합니다. '/install'이 기본값입니다. + +/passive | /quiet - 프롬프트 없이 최소 UI를 표시하거나 UI 및 + 프롬프트를 표시하지 않습니다. 기본적으로 UI와 모든 프롬프트가 표시됩니다. + +/norestart - 다시 시작하지 않게 합니다. 기본적으로 UI에서는 다시 시작하기 전에 묻는 메시지를 표시합니다. +/log [\[]"log.txt"[\]] - 특정 파일에 기록합니다. 기본적으로 로그 파일은 %TEMP%에 만들어집니다. + 닫기(&C) + 동의함(&A) + 옵션(&O) + 설치(&I) + 닫기(&C) + 설치 진행률 + 처리 중: + 초기화하는 중... + 취소(&C) + 설치 수정 + 복구(&R) + 제거(&U) + 닫기(&C) + 복구 완료됨 + 제거 완료됨 + 설치 완료 + 시작(&L) + 소프트웨어 설치를 완료하려면 컴퓨터를 다시 시작해야 합니다. + 다시 시작(&R) + 닫기(&C) + 설치 실패 + 설치 실패 + 제거 실패 + 복구 실패 + 하나 이상의 문제가 발생하여 설치하지 못했습니다. 문제를 해결한 다음 설치를 다시 시도하세요. 자세한 내용은 <a href="#">로그 파일</a>을 참조하세요 + 소프트웨어 롤백을 완료하려면 컴퓨터를 다시 시작해야 합니다. + 다시 시작(&R) + 닫기(&C) + 사용 중인 파일 + 다음의 응용 프로그램이 업데이트해야 할 파일을 사용 중입니다. + 응용 프로그램을 닫고 다시 시작합니다(&A). + 응용 프로그램을 닫지 않습니다(&D). 다시 부팅해야 합니다. + 확인(&O) + 취소(&C) + 설치가 완료되었습니다. + +다음 제품이 설치되었습니다. + • .NET SDK [DOTNETSDKVERSION] + • .NET 런타임 [DOTNETRUNTIMEVERSION] + • ASP.NET Core 런타임 [ASPNETCOREVERSION] + • .NET Windows 데스크톱 런타임 [WINFORMSANDWPFVERSION] + +이 제품은 사용량 현황 데이터를 수집합니다. + • 추가 정보 및 옵트아웃 https://aka.ms/dotnet-cli-telemetry + +리소스 + • .NET 설명서 https://aka.ms/dotnet-docs + • SDK 설명서 https://aka.ms/dotnet-sdk-docs + • 릴리스 정보 https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • 자습서 https://aka.ms/dotnet-tutorials + .NET SDK + + .NET SDK는 .NET 애플리케이션을 빌드, 실행 및 테스트하는 데 사용됩니다. 여러 언어, 편집기 및 개발자 도구 중에서 선택하고 대규모 라이브러리 에코시스템을 활용하여 웹, 모바일, 데스크톱, 게임 및 IoT용 앱을 빌드할 수 있습니다. .NET SDK를 유용하게 사용하시길 바랍니다. + .NET에 대한 자세한 정보 + 리소스 + <A HREF="https://aka.ms/dotnet-docs">.NET 설명서</A> + <A HREF="https://aka.ms/dotnet-cli-docs">SDK 설명서</A> + <A HREF="https://aka.ms/dev-privacy">개인정보처리방침</A> + <A HREF="https://aka.ms/dotnet-license-windows">.NET에 대한 라이선스 정보</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">원격 분석 수집 및 옵트아웃</A> + 설치 정보 + 프로젝트 복원 속도를 향상하고 오프라인 액세스를 사용할 수 있도록 하는 설치 프로세스 중 명령이 실행됩니다. 완료하는 데 최대 1분이 걸립니다. + + Visual Studio에서 .NET [VERSIONMAJOR].[VERSIONMINOR]를 사용하려는 경우 Visual Studio 2022 [MINIMUMVSVERSION] 이상이 필요합니다. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Learn more</A>. + + 설치를 클릭하면 다음 사용 약관에 동의하는 것입니다. + x64 SDK 설치의 설치 경로: " [DOTNETHOME_x64]" x86 SDK 설치의 경우와 같을 수 없습니다. " [DOTNETHOME_X86]" + ARM64 SDK 설치의 설치 경로: " [DOTNETHOME_ARM64]" x86 SDK 설치의 경우와 같을 수 없습니다. " [DOTNETHOME_X86]" + ARM64 SDK 설치의 설치 경로: " [DOTNETHOME_ARM64]" x64 SDK 설치의 경우와 같을 수 없습니다. " [DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1045/bundle.wxl b/src/redist/packaging/windows/LCID/1045/bundle.wxl new file mode 100644 index 000000000..ae7d35776 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1045/bundle.wxl @@ -0,0 +1,84 @@ + + Instalator pakietu [WixBundleName] + [BUNDLEMONIKER] + Czy na pewno chcesz anulować? + Poprzednia wersja + Instalator — Pomoc + /install | /repair | /uninstall | /layout [\[]"directory"[\]] — instaluje, naprawia, odinstalowuje + lub tworzy pełną lokalną kopię pakietu w katalogu. Domyślnie jest używany element „/install”. + +/passive | /quiet — wyświetla ograniczony interfejs użytkownika bez monitów albo nie wyświetla interfejsu użytkownika i + żadnych monitów. Domyślnie jest wyświetlany interfejs użytkownika oraz wszystkie monity. + +/norestart — pomija próby ponownego uruchomienia. Domyślnie interfejs użytkownika wyświetla monit przed ponownym uruchomieniem. +/log [\[]"log.txt"[\]] — tworzy dzienniki w określonym pliku. Domyślnie plik dziennika jest tworzony w katalogu %TEMP%. + &Zamknij + Zg&adzam się na warunki licencji + &Opcje + Za&instaluj + &Zamknij + Postęp instalacji + Przetwarzanie: + Trwa inicjowanie... + &Anuluj + Zmodyfikuj instalatora + Nap&raw + O&dinstaluj + &Zamknij + Pomyślnie ukończono naprawę + Pomyślnie ukończono operację odinstalowania + Instalacja przebiegła pomyślnie + &Uruchom + Aby zakończyć instalację oprogramowania, należy ponownie uruchomić komputer. + &Uruchom ponownie + &Zamknij + Instalacja nie powiodła się + Konfiguracja nie powiodła się + Operacja odinstalowania nie powiodła się + Naprawa nie powiodła się + Jeden lub więcej problemów spowodowało niepowodzenie instalacji. Napraw błędy, a następnie ponownie uruchom instalację. Aby uzyskać więcej informacji zobacz <a href="#">plik dziennika</a>. + Należy ponownie uruchomić komputer, aby dokończyć wycofywanie oprogramowania. + &Uruchom ponownie + &Zamknij + Pliki w użyciu + Następujące aplikacje korzystają z plików, które muszą zostać zaktualizowane: + Zamknij &aplikacje i spróbuj je ponownie uruchomić. + &Nie zamykaj aplikacji. Będzie konieczne ponowne uruchomienie. + &OK + &Anuluj + Instalacja zakończyła się pomyślnie. + +Następujące produkty zostały zainstalowane: + • Zestaw SDK .NET [DOTNETSDKVERSION] + • Środowisko uruchomieniowe platformy .NET [DOTNETRUNTIMEVERSION] + • Środowisko uruchomieniowe platformy ASP.NET Core [ASPNETCOREVERSION] + • Środowisko uruchomieniowe platformy .NET dla komputerów z systemem Windows [WINFORMSANDWPFVERSION] + +Ten produkt zbiera dane użycia + • Więcej informacji i rezygnacja: https://aka.ms/dotnet-cli-telemetry + +Zasoby + • Dokumentacja dotycząca platformy .NET https://aka.ms/dotnet-docs + • Dokumentacja dotycząca zestawu SDK https://aka.ms/dotnet-sdk-docs + • Informacje o wersji https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Samouczki https://aka.ms/dotnet-tutorials + .NET SDK + + Zestaw SDK platformy .NET służy do tworzenia, uruchamiania i testowania aplikacji platformy .NET. Możesz wybierać spośród wielu języków, edytorów i narzędzi deweloperskich oraz korzystać z dużego ekosystemu bibliotek do tworzenia aplikacji internetowych, mobilnych, na komputer, gry i Internet rzeczy. Mamy nadzieję, że Ci się to spodoba! + Dowiedz się więcej o platformie .NET + Zasoby + <A HREF="https://aka.ms/dotnet-docs">Dokumentacja platformy .NET</A> + <A HREF="https://aka.ms/dotnet-cli-docs">Dokumentacja zestawu SDK</A> + <A HREF="https://aka.ms/dev-privacy">Zasady zachowania poufności informacji</A> + <A HREF="https://aka.ms/dotnet-license-windows">Informacje o licencjonowaniu dla platformy .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Zbieranie telemetrii i rezygnacja</A> + Uwaga dotycząca instalacji + W trakcie procesu instalacji zostanie uruchomione polecenie, które zwiększy szybkość przywracania projektu i umożliwi dostęp do trybu offline. Zajmie to maksymalnie minutę. + + Jeśli planujesz używać platformy .NET [VERSIONMAJOR].[VERSIONMINOR] z programem Visual Studio, wymagany jest program Visual Studio 2022 [MINIMUMVSVERSION] lub nowszy. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Learn more</A>. + + Klikając pozycję Zainstaluj, wyrażasz zgodę na następujące warunki: + Ścieżka instalacji w przypadku instalacji zestawu SDK x64: „[DOTNETHOME_X64]” nie może być taka sama jak w przypadku instalacji zestawu SDK x86: „[DOTNETHOME_X86]” + Ścieżka instalacji w przypadku instalacji zestawu SDK ARM64: „[DOTNETHOME_ARM64]” nie może być taka sama jak w przypadku instalacji zestawu SDK X86: „[DOTNETHOME_X86]” + Ścieżka instalacji w przypadku instalacji zestawu SDK ARM64: „[DOTNETHOME_ARM64]” nie może być taka sama jak w przypadku instalacji zestawu SDK x64: „[DOTNETHOME_X64]” + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1046/bundle.wxl b/src/redist/packaging/windows/LCID/1046/bundle.wxl new file mode 100644 index 000000000..9b8f73941 --- /dev/null +++ b/src/redist/packaging/windows/LCID/1046/bundle.wxl @@ -0,0 +1,84 @@ + + Instalador do [WixBundleName] + [BUNDLEMONIKER] + Tem certeza de que deseja cancelar? + Versão anterior + Ajuda da Instalação + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - instala, repara, desinstala + ou cria uma cópia local completa do pacote no diretório. '/install' é o padrão. + +/passive | /quiet - exibe a interface do usuário mínima sem prompts ou não exibe a interface do usuário e + nenhum prompt. Por padrão, a interface do usuário e todos os prompts são exibidos. + +/norestart - suprimi qualquer tentativa de reiniciar. Por padrão, a interface do usuário será solicitada antes de reiniciar. +/log [\[]"log.txt"[\]] - logs para um arquivo específico. Por padrão, um arquivo de log é criado em %TEMP%. + &Fechar + Eu &concordo com os termos e condições da licença + &Opções + &Instalar + &Fechar + Progresso da Instalação + Processando: + Inicializando... + &Cancelar + Modificar Instalação + &Reparar + &Desinstalar + &Fechar + Reparação Concluída com Êxito + Desinstalação Concluída com Êxito + Instalação com Êxito + &Iniciar + É necessário reiniciar o computador para concluir a instalação do software. + &Reiniciar + &Fechar + Falha na Instalação + Falha na Instalação + Falha na Desinstalação + Falha ao Reparar + Um ou mais problemas fizeram com que a configuração falhasse. Corrija os problemas e tente novamente a configuração. Para obter mais informações, consulte o <a href="#">arquivo de log</a>. + Reinicie o computador para concluir a reversão do software. + &Reiniciar + &Fechar + Arquivos em Uso + Os aplicativos a seguir estão usando arquivos que precisam ser atualizados: + Feche os &aplicativos e tente reiniciá-los. + &Não feche os aplicativos. Uma reinicialização será necessária. + &OK + &Cancelar + A instalação foi bem sucedida. + +Os seguintes produtos foram instalados: + • .NET SDK [DOTNETSDKVERSION] + • .NET Runtime [DOTNETRUNTIMEVERSION] + • ASP.NET Core Runtime [ASPNETCOREVERSION] + • .NET Windows Desktop Runtime [WINFORMSANDWPFVERSION] + +Esse produto coleta dados de uso + • Mais informações e recusa https://aka.ms/dotnet-cli-telemetry + +Recursos + • Documentação do .NET https://aka.ms/dotnet-docs + • Documentação do SDK https://aka.ms/dotnet-sdk-docs + • Notas de Versão https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Tutoriais https://aka.ms/dotnet-tutorials + SDK do .NET + + O SDK do .NET é usado para criar, executar e testar aplicativos .NET. Você pode escolher entre vários idiomas, editores e ferramentas de desenvolvedor e aproveitar um grande ecossistema de bibliotecas para criar aplicativos para Web, dispositivos móveis, desktop, jogos e IoT. Esperamos que você goste! + Saiba mais sobre o .NET + Recursos + <A HREF="https://aka.ms/dotnet-docs">Documentação do .NET</A> + <A HREF="https://aka.ms/dotnet-cli-docs">Documentação do SDK</A> + <A HREF="https://aka.ms/dev-privacy">Política de Privacidade</A> + <A HREF="https://aka.ms/dotnet-license-windows">Informações de licenciamento para .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Coleção e recusa de telemetria</A> + Nota de instalação + Um comando será executado durante o processo de instalação que melhorará a velocidade de restauração do projeto e habilitará o acesso offline. Isso levará até um minuto para ser concluído. + + Se você planeja usar .NET [VERSIONMAJOR].[VERSIONMINOR] com o Visual Studio, é necessário o Visual Studio 2022 [MINIMUMVSVERSION] ou mais recente. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Saiba mais</A>. + + Ao clicar em Instalar, você concorda com os termos a seguir: + O caminho da instalação para instalações do SDK x64: "[DOTNETHOME_X64]" não pode ser o mesmo que para instalações do SDK x86: "[DOTNETHOME_X86]" + O caminho da instalação para instalações do SDK ARM64: "[DOTNETHOME_ARM64]" não pode ser o mesmo que para instalações do SDK x86: "[DOTNETHOME_X86]" + O caminho da instalação para instalações do SDK ARM64: "[DOTNETHOME_ARM64]" não pode ser o mesmo que para instalações do SDK x64: "[DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1049/bundle.wxl b/src/redist/packaging/windows/LCID/1049/bundle.wxl new file mode 100644 index 000000000..de56ea27a --- /dev/null +++ b/src/redist/packaging/windows/LCID/1049/bundle.wxl @@ -0,0 +1,84 @@ + + Установщик [WixBundleName] + [BUNDLEMONIKER] + Отменить? + Предыдущая версия + Справка по установке + /install | /repair | /uninstall | /layout [\[]"directory"[\]] — установка, восстановление, удаление + или создание полной локальной копии пакета в каталоге. По умолчанию — "/install". + +/passive | /quiet — отображение минимального пользовательского интерфейса без запросов или работы без пользовательского интерфейса и + без запросов. По умолчанию отображаются пользовательский интерфейс и все запросы. + +/norestart — отключение всех попыток перезагрузки. По умолчанию в пользовательском интерфейсе перед перезагрузкой отображается запрос. +/log [\[]"log.txt"[\]] — запись журнала в указанный файл. По умолчанию файл журнала создается в папке %TEMP%. + &Закрыть + Я &принимаю условия лицензии + &Параметры + &Установить + &Закрыть + Ход установки + Обработка: + Инициализация... + Отм&ена + Изменение установки + &Исправить + &Удалить + &Закрыть + Исправление успешно завершено + Удаление успешно завершено + Установка успешно завершена + &Запустить + Для завершения установки программного обеспечения нужно перезагрузить компьютер. + &Перезапустить + &Закрыть + Сбой установки + Сбой установки + Сбой удаления + Сбой восстановления + Одна или несколько проблем вызывали сбой программы установки. Исправьте эти проблемы и попробуйте повторить установку. Дополнительные сведения см. в <a href="#">файле журнала</a>. + Необходимо перезагрузить компьютер, чтобы завершить откат программного обеспечения. + &Перезапустить + &Закрыть + Используемые файлы + Следующие приложения используют файлы, которые следует обновить: + Закройте &приложения и попробуйте перезапустить их. + &Не закрывайте приложения. Потребуется перезагрузка компьютера. + &ОК + &Отменить + Установка выполнена. + +Установлены следующие компоненты: + • Пакет SDK для .NET [DOTNETSDKVERSION] + • Среда выполнения .NET [DOTNETRUNTIMEVERSION] + • Среда выполнения ASP.NET Core [ASPNETCOREVERSION] + • Среда выполнения .NET для Windows Desktop [WINFORMSANDWPFVERSION] + +Этот продукт собирает данные об использовании. + • Чтобы получить дополнительные сведения или отказаться от использования продукта, перейдите на страницу https://aka.ms/dotnet-cli-telemetry + +Ресурсы + • Документация по .NET: https://aka.ms/dotnet-docs + • Документация по SDK: https://aka.ms/dotnet-sdk-docs + • Заметки о выпуске: https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Учебники: https://aka.ms/dotnet-tutorials + Пакет SDK для .NET + + Пакет SDK для .NET используется для сборки, запуска и тестирования приложений .NET. Вы можете выбрать один из нескольких языков, использовать различные редакторы и инструменты для разработчиков, а также воспользоваться преимуществами большой экосистемы библиотек для создания веб-приложений, мобильных и классических приложений, игр и приложений Интернета вещей. Надеемся, вам понравится! + Дополнительные сведения о .NET + Ресурсы + <A HREF="https://aka.ms/dotnet-docs">Документация по .NET</A> + <A HREF="https://aka.ms/dotnet-cli-docs">Документация по пакету SDK</A> + <A HREF="https://aka.ms/dev-privacy">Заявление о конфиденциальности</A> + <A HREF="https://aka.ms/dotnet-license-windows">Сведения о лицензировании .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Сбор данных телеметрии и отказ от использования</A> + Примечание по установке + В процессе установки будет выполнена команда, которая увеличит скорость восстановления проекта и обеспечит автономный доступ. Выполнение займет до минуты. + + Если вы планируете использовать .NET [VERSIONMAJOR].[VERSIONMINOR] с Visual Studio, требуется Visual Studio 2022 версии [MINIMUMVSVERSION] или более поздней. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Дополнительные сведения</A>. + + Нажимая кнопку "Установить", вы принимаете следующие условия. + Совпадение пути установки недопустимо для установок пакетов SDK x64: "[DOTNETHOME_X64]" и SDK x86: "[DOTNETHOME_X86]". + Совпадение пути установки недопустимо для установок пакетов SDK ARM64: "[DOTNETHOME_ARM64]" и SDK x86: "[DOTNETHOME_X86]". + Совпадение пути установки недопустимо для установок пакетов SDK ARM64: "[DOTNETHOME_ARM64]" и SDK x64: "[DOTNETHOME_X64]". + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/1055/bundle.wxl b/src/redist/packaging/windows/LCID/1055/bundle.wxl new file mode 100644 index 000000000..1dc8bf22c --- /dev/null +++ b/src/redist/packaging/windows/LCID/1055/bundle.wxl @@ -0,0 +1,84 @@ + + [WixBundleName] Yükleyicisi + [BUNDLEMONIKER] + İptal etmek istediğinizden emin misiniz? + Önceki sürüm + Kurulum Yardımı + /install | /repair | /uninstall | /layout [\[]"dizin"[\]] - yükler, onarır, kaldırır + ya da dizindeki paketin tam bir yerel kopyasını oluşturur. Varsayılan '/install' değeridir. + +/passive | /quiet - en az düzeyde istemsiz UI gösterir ya da hiç UI göstermez ve + istem yoktur. Varsayılan olarak, UI ve tüm istemler görüntülenir. + +/norestart - yeniden başlama denemelerini durdurur. Varsayılan olarak UI yeniden başlatılmadan önce sorar. +/log [\[]"log.txt"[\]] - belirli bir günlük dosyası tutar. Varsayılan olarak, %TEMP% içinde bir günlük dosyası oluşturulur. + &Kapat + Lisans hüküm ve koşullarını &kabul ediyorum + &Seçenekler + Yü&kle + &Kapat + Kurulum İlerleme Durumu + İşleniyor: + Başlatılıyor... + İ&ptal + Kurulumu değiştir + &Onar + &Kaldır + &Kapat + Onarım Başarıyla Tamamlandı + Kaldırma Başarıyla Tamamlandı + Kurulum Başarılı + &Başlat + Yazılım yükleme işlemini tamamlamak için bilgisayarınızı yeniden başlatmanız gerekir. + &Yeniden Başlat + &Kapat + Kurulum Başarısız + Kurulum Başarısız + Kaldırma Başarısız + Onarım Başarısız + Bir ya da daha fazla sorun nedeniyle kurulum başarısız oldu. Lütfen bu sorunları düzeltin ve kurulumu yeniden deneyin. Daha fazla bilgi için <a href="#">günlük dosyasına</a> bakın. + Yazılımın geri alınmasını tamamlamak için bilgisayarınızı yeniden başlatmanız gerekiyor. + &Yeniden Başlat + &Kapat + Kullanımda Olan Dosyalar + Şu uygulamalar güncelleştirilmesi gereken dosyaları kullanıyor: + &Uygulamaları kapatın ve yeniden başlatmayı deneyin. + &Uygulamaları kapatmayın. Sistemi yeniden başlatmanız gerekir. + &Tamam + İ&ptal + Yükleme başarılı oldu. + +Aşağıdakiler ürünler yüklendi: + • .NET SDK [DOTNETSDKVERSION] + • .NET Çalışma Zamanı [DOTNETRUNTIMEVERSION] + • ASP.NET Core Çalışma Zamanı [ASPNETCOREVERSION] + • .NET Windows Masaüstü Çalışma Zamanı [WINFORMSANDWPFVERSION] + +Bu ürün, kullanım verilerini toplar + • Daha fazla bilgi ve katılmamayı seçmek için bkz. https://aka.ms/dotnet-cli-telemetry + +Kaynaklar + • .NET Belgeleri https://aka.ms/dotnet-docs + • SDK Belgeleri https://aka.ms/dotnet-sdk-docs + • Sürüm Notları https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Öğreticiler https://aka.ms/dotnet-tutorials + .NET SDK + + .NET SDK, .NET uygulamalarını derlemek, çalıştırmak ve test etmek için kullanılır. Birden çok dil, düzenleyici ve geliştirici aracı arasından seçim yapabilirsiniz ve web, mobil, masaüstü, oyun ve IoT uygulamaları oluşturmak için büyük bir kitaplık ekosisteminden yararlanabilirsiniz. Beğeneceğinizi umuyoruz! + .NET hakkında daha fazla bilgi edinin + Kaynaklar + <A HREF="https://aka.ms/dotnet-docs">.NET Belgeleri</A> + <A HREF="https://aka.ms/dotnet-cli-docs">SDK Belgeleri</A> + <A HREF="https://aka.ms/dev-privacy">Gizlilik Bildirimi</A> + <A HREF="https://aka.ms/dotnet-license-windows">.NET için Lisans Bilgileri</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Telemetri koleksiyonu ve katılmamayı seçme</A> + Yükleme notu + Yükleme işlemi sırasında, proje geri yükleme hızını artıran ve çevrimdışı erişimi etkinleştiren bir komut çalıştırılır. Tamamlanması bir dakikanızı alır. + + Visual Studio ile .NET [VERSIONMAJOR].[VERSIONMINOR] kullanmayı planlıyorsanız Visual Studio 2022 [MINIMUMVSVERSION] veya üzeri bir sürüm gerekir. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Daha fazla bilgi edinin</A>. + + Yükle'ye tıklayarak aşağıdaki koşulları kabul etmiş olursunuz: + x64 SDK yüklemelerinin yükleme yolu ("[DOTNETHOME_X64]"), x86 SDK yüklemelerinin yükleme yolu ("[DOTNETHOME_X86]") ile aynı olamaz + ARM64 SDK yüklemeleri için yükleme yolu ("[DOTNETHOME_ARM64]"), x86 SDK yüklemelerinin yükleme yolu ("[DOTNETHOME_X86]") ile aynı olamaz + ARM64 SDK yüklemelerinin yükleme yolu ("[DOTNETHOME_ARM64]"), x64 SDK yüklemelerinin yükleme yolu ("[DOTNETHOME_X64]") ile aynı olamaz + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/2052/bundle.wxl b/src/redist/packaging/windows/LCID/2052/bundle.wxl new file mode 100644 index 000000000..fcb7dc9a9 --- /dev/null +++ b/src/redist/packaging/windows/LCID/2052/bundle.wxl @@ -0,0 +1,84 @@ + + [WixBundleName] 安装程序 + [BUNDLEMONIKER] + 是否确实要取消? + 上一版本 + 安装程序帮助 + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 在目录中安装、修复、卸载 + 或创建安装包的完整本地副本。'/Install' 为默认选择。 + +/passive | /quiet - 显示最少的 UI 且无提示,或不显示 UI 且 + 无提示。默认显示 UI 及全部提示。 + +/norestart - 禁止任何重新启动。默认在重新启动前显示提示 UI。 +/log [\[]"log.txt"[\]] - 向特定文件写入日志。默认在 %TEMP% 中创建日志文件。 + 关闭(&C) + 我同意许可条款和条件(&A) + 选项(&O) + 安装(&I) + 关闭(&C) + 安装进度 + 正在处理: + 正在初始化... + 取消(&C) + 修改安装程序 + 修复(&R) + 卸载(&U) + 关闭(&C) + 成功完成了修复 + 成功完成了卸载 + 设置成功 + 启动(&L) + 必须重启计算机才能完成软件安装。 + 重新启动(&R) + 关闭(&C) + 安装失败 + 安装失败 + 卸载失败 + 修复失败 + 一个或多个问题导致了安装失败。请修复这些问题,然后重试安装。有关详细信息,请参阅<a href="#">日志文件</a>。 + 必须重新启动计算机才能完成软件回退。 + 重新启动(&R) + 关闭(&C) + 文件正在使用 + 以下应用程序正在使用的文件需要更新: + 关闭应用程序并尝试重启(&A)。 + 不关闭应用程序(&D)。需要重启。 + 确定(&O) + 取消(&C) + 已成功安装。 + +下列产品已安装: + • .NET SDK [DOTNETSDKVERSION] + • .NET Runtime [DOTNETRUNTIMEVERSION] + • ASP.NET Core Runtime [ASPNETCOREVERSION] + • .NET Windows Desktop Runtime [WINFORMSANDWPFVERSION] + +此产品会收集用法数据 + • 详细信息和选择退出选项: https://aka.ms/dotnet-cli-telemetry + +资源 + • .NET 文档: https://aka.ms/dotnet-docs + • SDK 文档: https://aka.ms/dotnet-sdk-docs + • 发行说明: https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • 教程: https://aka.ms/dotnet-tutorials + .NET SDK + + .NET SDK 用于生成、运行和测试 .NET 应用程序。有多种语言、编辑器和开发人员工具可供选择,你也可使用由库构成的大型生态系统来构建面向 Web、移动设备、桌面、游戏和 IoT 的应用。希望你喜欢它! + 了解有关 .NET 的详细信息 + 资源 + <A HREF="https://aka.ms/dotnet-docs">.NET 文档</A> + <A HREF="https://aka.ms/dotnet-cli-docs">SDK 文档</A> + <A HREF="https://aka.ms/dev-privacy">隐私声明</A> + <A HREF="https://aka.ms/dotnet-license-windows">.NET 的许可信息</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">遥测集合和选择退出选项</A> + 安装说明 + 将在要提升项目还原速度并实现脱机访问的安装进程期间运行命令。此操作最多 1 分钟即可完成。 + + 如果你计划使用 .NET [VERSIONMAJOR].[VERSIONMINOR] 与 Visual Studio、Visual Studio 2022 [MINIMUMVSVERSION] 或更高版本是必需的。<A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">了解详细信息</A>。 + + 单击“安装”即表示你同意以下条款: + x64 SDK 安装的安装路径: "[DOTNETHOME_X64]" 不能与 x86 SDK 安装的路径相同: "[DOTNETHOME_X86]" + ARM64 SDK 安装的安装路径: "[DOTNETHOME_ARM64]" 不能与 x86 SDK 安装的路径相同: "[DOTNETHOME_X86]" + ARM64 SDK 安装的安装路径: "[DOTNETHOME_ARM64]" 不能与 x64 SDK 安装的路径相同: "[DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/LCID/3082/bundle.wxl b/src/redist/packaging/windows/LCID/3082/bundle.wxl new file mode 100644 index 000000000..7b90e9f0d --- /dev/null +++ b/src/redist/packaging/windows/LCID/3082/bundle.wxl @@ -0,0 +1,85 @@ + + Instalador de [WixBundleName] + [BUNDLEMONIKER] + ¿Está seguro de que desea cancelar la operación? + Versión anterior + Ayuda del programa de instalación + /install | /repair | /uninstall | /layout [\[]"directory"[\]] - instala, repara, desinstala + + o crea una copia local completa del bundle en directorio. '/install' es la opción por defecto. + +/passive | /quiet - muestra una interfaz de usuario mínima sin avisos o no muestra interfaz de usuario ni + ni indicaciones. La interfaz de usuario y todos los avisos son mostrados de forma predeterminada. + +/norestart - suprime cualquier intento de reinicio. La interfaz de usuario preguntará antes de reiniciar por defecto. +/log [\[]"log.txt"[\]] - registra en un archivo específico. Un archivo de registro es creado de forma predeterminada en %TEMP%. + &Cerrar + &Acepto los términos y condiciones de licencia + &Opciones + &Instalar + &Cerrar + Progreso de la instalación + Procesando: + Inicializando... + &Cancelar + Modificar instalación + &Reparar + &Desinstalar + &Cerrar + La reparación se completó correctamente + La desinstalación se completó correctamente + La instalación o desinstalación se realizó correctamente + &Iniciar + Debe reiniciar el equipo para finalizar la instalación del software. + &Reiniciar + &Cerrar + Error de instalación + Error de instalación + No se pudo desinstalar + No se pudo reparar + Uno o varios problemas han provocado un error en la instalación. Solucione los problemas e intente de nuevo la instalación. Para obtener más información, consulte e <a href="#">log file</a>. + Debe reiniciar el equipo para completar la reversión del software. + &Reiniciar + &Cerrar + Archivos en uso + Las siguientes aplicaciones usan archivos que se deben actualizar: + Cerrar las &aplicaciones e intentar reiniciarlas. + &No cerrar las aplicaciones. Será necesario un reinicio. + &Aceptar + &Cancelar + La instalación se ha realizado correctamente. + +Se han instalado los siguientes productos: + • .NET SDK [DOTNETSDKVERSION] + • .NET Runtime [DOTNETRUNTIMEVERSION] + • Tiempo de ejecución de ASP.NET Core [ASPNETCOREVERSION] + • Tiempo de ejecución de .NET Windows Desktop [WINFORMSANDWPFVERSION] + +Este producto recopila datos de uso + •Más información y exclusión voluntaria https://aka.ms/dotnet-cli-telemetry + +Recursos + • Documentación de .NET https://aka.ms/dotnet-docs + • Documentación del SDK https://aka.ms/dotnet-sdk-docs + • Notas de la versión https://aka.ms/dotnet[VERSIONMAJOR]-release-notes + • Tutoriales https://aka.ms/dotnet-tutorials + SDK de .NET + + El SDK de .NET se utiliza para crear, ejecutar y probar aplicaciones .NET. Puede elegir entre varios lenguajes, editores y herramientas de desarrollo, y aprovechar un amplio ecosistema de bibliotecas para crear aplicaciones para web, móvil, escritorio, juegos e IoT. Esperamos que lo disfrutes. + Más información sobre .NET + Recursos + <A HREF="https://aka.ms/dotnet-docs">Documentación de .NET</A> + <A HREF="https://aka.ms/dotnet-cli-docs">Documentación del SDK</A> + <A HREF="https://aka.ms/dev-privacy">Declaración de privacidad</A> + <A HREF="https://aka.ms/dotnet-license-windows">Información de licencias de .NET</A> + <A HREF="https://aka.ms/dotnet-cli-telemetry">Recopilación de telemetría y cómo desactivarla</A> + Nota de instalación + Se ejecutará un comando durante el proceso de instalación que mejorará la velocidad de restauración del proyecto y permitirá el acceso sin conexión. La operación tardará hasta un minuto en completarse. + + Si tiene previsto utilizar .NET [VERSIONMAJOR].[VERSIONMINOR] con Visual Studio, necesitará Visual Studio 2022 [MINIMUMVSVERSION] o una versión más reciente. <A HREF="https://aka.ms/dotnet[VERSIONMAJOR]-release-notes">Learn more</A>. + + Al hacer clic en Instalar, acepta los siguientes términos: + La ruta de instalación para las instalaciones del SDK x64: "[DOTNETHOME_X64]" no puede ser la misma que para las instalaciones del SDK x86: "[DOTNETHOME_X86]" + La ruta de instalación para las instalaciones del SDK ARM64: "[DOTNETHOME_ARM64]" no puede ser la misma que para las instalaciones del SDK x86: "[DOTNETHOME_X86]" + La ruta de instalación para instalaciones del SDK ARM64: "[DOTNETHOME_ARM64]" no puede ser la misma que para las instalaciones del SDK x64: "[DOTNETHOME_X64]" + \ No newline at end of file diff --git a/src/redist/packaging/windows/StableFileIdForApphostTransform.xslt b/src/redist/packaging/windows/StableFileIdForApphostTransform.xslt new file mode 100644 index 000000000..dcded1aca --- /dev/null +++ b/src/redist/packaging/windows/StableFileIdForApphostTransform.xslt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + apphosttemplateapphostexe + + + + diff --git a/src/redist/packaging/windows/VS.Redist.Common.Net.Core.SDK.MSBuildExtensions.nuspec b/src/redist/packaging/windows/VS.Redist.Common.Net.Core.SDK.MSBuildExtensions.nuspec new file mode 100644 index 000000000..3e0238abd --- /dev/null +++ b/src/redist/packaging/windows/VS.Redist.Common.Net.Core.SDK.MSBuildExtensions.nuspec @@ -0,0 +1,17 @@ + + + VS.Redist.Common.Net.Core.SDK.MSBuildExtensions + 1.0.0 + VS.Redist.Common.Net.Core.SDK.MSBuildExtensions + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + https://github.com/dotnet/core-sdk + true + MSBuild extensions bundled with .NET Core SDK for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + diff --git a/src/redist/packaging/windows/VS.Redist.Common.Net.Core.SDK.VSTemplateLocator.nuspec b/src/redist/packaging/windows/VS.Redist.Common.Net.Core.SDK.VSTemplateLocator.nuspec new file mode 100644 index 000000000..8c23b34d4 --- /dev/null +++ b/src/redist/packaging/windows/VS.Redist.Common.Net.Core.SDK.VSTemplateLocator.nuspec @@ -0,0 +1,17 @@ + + + VS.Redist.Common.Net.Core.SDK.VSTemplateLocator + 1.0.0 + VS.Redist.Common.Net.Core.SDK.VSTemplateLocator + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + https://github.com/dotnet/core-sdk + true + MSBuild extensions bundled with .NET Core SDK for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + diff --git a/src/redist/packaging/windows/VS.Redist.Common.NetCore.SdkPlaceholder.nuspec b/src/redist/packaging/windows/VS.Redist.Common.NetCore.SdkPlaceholder.nuspec new file mode 100644 index 000000000..7c852a0d9 --- /dev/null +++ b/src/redist/packaging/windows/VS.Redist.Common.NetCore.SdkPlaceholder.nuspec @@ -0,0 +1,17 @@ + + + VS.Redist.Common.NetCore.SdkPlaceholder.$ARCH$.$MAJOR_MINOR$ + 1.0.0 + VS.Redist.Common.NetCore.SdkPlaceholder.$ARCH$.$MAJOR_MINOR$ + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + https://github.com/dotnet/core-sdk + true + .NET Core $MAJOR_MINOR$ SDK ARP Placeholder ($ARCH$) Windows Installer MSI as a .nupkg for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + diff --git a/src/redist/packaging/windows/VS.Redist.Common.NetCore.Templates.nuspec b/src/redist/packaging/windows/VS.Redist.Common.NetCore.Templates.nuspec new file mode 100644 index 000000000..b12cea3c8 --- /dev/null +++ b/src/redist/packaging/windows/VS.Redist.Common.NetCore.Templates.nuspec @@ -0,0 +1,17 @@ + + + VS.Redist.Common.NetCore.Templates.$ARCH$.$MAJOR_MINOR$ + 1.0.0 + VS.Redist.Common.NetCore.Templates.$ARCH$.$MAJOR_MINOR$ + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + https://github.com/dotnet/core-sdk + true + .NET Core $MAJOR_MINOR$ Templates Windows Installer MSI as a .nupkg for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + diff --git a/src/redist/packaging/windows/VS.Redist.Common.NetCore.Toolset.nuspec b/src/redist/packaging/windows/VS.Redist.Common.NetCore.Toolset.nuspec new file mode 100644 index 000000000..3047a8566 --- /dev/null +++ b/src/redist/packaging/windows/VS.Redist.Common.NetCore.Toolset.nuspec @@ -0,0 +1,17 @@ + + + VS.Redist.Common.NetCore.Toolset.$ARCH$.$MAJOR_MINOR$ + 1.0.0 + VS.Redist.Common.NetCore.Toolset.$ARCH$.$MAJOR_MINOR$ + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + https://github.com/dotnet/core-sdk + true + .NET Core $MAJOR_MINOR$ SDK Toolset ($ARCH$) Windows Installer MSI as a .nupkg for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + diff --git a/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.Resolver.cxspec b/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.Resolver.cxspec new file mode 100644 index 000000000..958ba2939 --- /dev/null +++ b/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.Resolver.cxspec @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.Resolver.nuspec b/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.Resolver.nuspec new file mode 100644 index 000000000..2703fb4d4 --- /dev/null +++ b/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.Resolver.nuspec @@ -0,0 +1,18 @@ + + + VS.Tools.Net.Core.SDK.Resolver + 1.0.0 + VS.Tools.Net.Core.SDK.Resolver + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + https://github.com/dotnet/core-sdk + true + .NET Core SDK MSBuild resolver as a .nupkg for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + + diff --git a/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.nuspec b/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.nuspec new file mode 100644 index 000000000..49cecae99 --- /dev/null +++ b/src/redist/packaging/windows/VS.Tools.Net.Core.SDK.nuspec @@ -0,0 +1,18 @@ + + + VS.Tools.Net.Core.SDK.$ARCH$ + 1.0.0 + VS.Tools.Net.Core.SDK.$ARCH$ + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + https://github.com/dotnet/core-sdk + true + .NET Core SDK ($ARCH$) as a .nupkg for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + + diff --git a/src/redist/packaging/windows/bundle.thm b/src/redist/packaging/windows/bundle.thm new file mode 100644 index 000000000..ffaf5c026 --- /dev/null +++ b/src/redist/packaging/windows/bundle.thm @@ -0,0 +1,95 @@ + + #(loc.Caption) + Segoe UI + Segoe UI + Segoe UI + Segoe UI + Segoe UI + Segoe UI + + #(loc.Title) + + + + + + #(loc.HelpHeader) + #(loc.HelpText) + + + + + + + + #(loc.WelcomeHeaderMessage) + #(loc.WelcomeDescription) + #(loc.VisualStudioWarning) + #(loc.LicenseAssent) + #(loc.PrivacyStatementLink) + #(loc.DotNetCLITelemetryLink) + #(loc.DotNetEulaLink) + + + + + + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + + + + + + + + + #(loc.ProgressHeader) + #(loc.ProgressLabel) + #(loc.OverallProgressPackageText) + + + + + + + + #(loc.ModifyHeader) + + + + + + + + + #(loc.SuccessHeader) + #(loc.FirstTimeWelcomeMessage) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) + + #(loc.SuccessRestartText) + + + + + + + + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) + #(loc.FailureHyperlinkLogText) + + #(loc.FailureRestartText) + + + + diff --git a/src/redist/packaging/windows/bundle.wxs b/src/redist/packaging/windows/bundle.wxs new file mode 100644 index 000000000..24051645d --- /dev/null +++ b/src/redist/packaging/windows/bundle.wxs @@ -0,0 +1,256 @@ + + + + + + + WixBundleInstalled OR (NOT DOTNETHOME_X64 ~= DOTNETHOME_X86) OR DOTNETHOMESIMILARITYCHECKOVERRIDE + + + + WixBundleInstalled OR (NOT DOTNETHOME_ARM64 ~= DOTNETHOME_X86) OR DOTNETHOMESIMILARITYCHECKOVERRIDE + + + + + WixBundleInstalled OR (NOT DOTNETHOME_ARM64 ~= DOTNETHOME_X64) OR (NOT NativeMachine="$(var.NativeMachine_arm64)") OR DOTNETHOMESIMILARITYCHECKOVERRIDE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eula.rtf + + + + + diff --git a/src/redist/packaging/windows/dotnet.wxs b/src/redist/packaging/windows/dotnet.wxs new file mode 100644 index 000000000..4c04ccd8f --- /dev/null +++ b/src/redist/packaging/windows/dotnet.wxs @@ -0,0 +1,79 @@ + + + + + + + Installed OR ALLOWMSIINSTALL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT Installed + NOT Installed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/packaging/windows/dotnethome_x64.wxs b/src/redist/packaging/windows/dotnethome_x64.wxs new file mode 100644 index 000000000..e8a9b5cc0 --- /dev/null +++ b/src/redist/packaging/windows/dotnethome_x64.wxs @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + VersionNT64 OR + + WIX_NATIVE_MACHINE AND NOT WIX_NATIVE_MACHINE="$(var.InstallerNativeMachine)" + + + + + + + + NON_NATIVE_ARCHITECTURE AND NOT DOTNETHOME + + + + \ No newline at end of file diff --git a/src/redist/packaging/windows/dummyeula.rtf b/src/redist/packaging/windows/dummyeula.rtf new file mode 100644 index 000000000..db115770e Binary files /dev/null and b/src/redist/packaging/windows/dummyeula.rtf differ diff --git a/src/redist/packaging/windows/generatebundle.ps1 b/src/redist/packaging/windows/generatebundle.ps1 new file mode 100644 index 000000000..d86377167 --- /dev/null +++ b/src/redist/packaging/windows/generatebundle.ps1 @@ -0,0 +1,170 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +param( + [Parameter(Mandatory=$true)][string]$WorkloadManifestWxsFile, + [Parameter(Mandatory=$true)][string]$CLISDKMSIFile, + [Parameter(Mandatory=$true)][string]$ASPNETRuntimeWixLibFile, + [Parameter(Mandatory=$true)][string]$SharedFxMSIFile, + [Parameter(Mandatory=$true)][string]$HostFxrMSIFile, + [Parameter(Mandatory=$true)][string]$SharedHostMSIFile, + [Parameter(Mandatory=$true)][string]$WinFormsAndWpfMSIFile, + [Parameter(Mandatory=$true)][string]$NetCoreAppTargetingPackMSIFile, + [Parameter(Mandatory=$true)][string]$NetStandardTargetingPackMSIFile, + [Parameter(Mandatory=$true)][string]$NetCoreAppHostPackMSIFile, + [Parameter(Mandatory=$true)][string]$AlternateNetCoreAppHostPackMSIFile, + [Parameter(Mandatory=$true)][string]$Arm64NetCoreAppHostPackMSIFile, + [Parameter(Mandatory=$true)][string]$AspNetTargetingPackMSIFile, + [Parameter(Mandatory=$true)][string]$WindowsDesktopTargetingPackMSIFile, + [Parameter(Mandatory=$true)][string]$FinalizerExe, + [Parameter(Mandatory=$true)][string]$TemplatesMSIFile, + [Parameter(Mandatory=$true)][string]$DotnetBundleOutput, + [Parameter(Mandatory=$true)][string]$WixRoot, + [Parameter(Mandatory=$true)][string]$ProductMoniker, + [Parameter(Mandatory=$true)][string]$DotnetMSIVersion, + [Parameter(Mandatory=$true)][string]$SDKBundleVersion, + [Parameter(Mandatory=$true)][string]$MinimumVSVersion, + [Parameter(Mandatory=$true)][string]$DotnetCLINugetVersion, + [Parameter(Mandatory=$true)][string]$VersionMajor, + [Parameter(Mandatory=$true)][string]$VersionMinor, + [Parameter(Mandatory=$true)][string]$WindowsDesktopVersion, + [Parameter(Mandatory=$true)][string]$UpgradeCode, + [Parameter(Mandatory=$true)][string]$DependencyKeyName, + [Parameter(Mandatory=$true)][string]$Architecture, + [Parameter(Mandatory=$true)][string]$DotNetRuntimeVersion, + [Parameter(Mandatory=$true)][string]$AspNetCoreVersion, + [Parameter(Mandatory=$true)][string]$SDKProductBandVersion +) + +function RunCandleForBundle +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running candle for bundle.." + + $candleOutput = .\candle.exe -nologo ` + -dDotnetSrc="$inputDir" ` + -dMicrosoftEula="$PSScriptRoot\dummyeula.rtf" ` + -dProductMoniker="$ProductMoniker" ` + -dBuildVersion="$DotnetMSIVersion" ` + -dSDKBundleVersion="$SDKBundleVersion" ` + -dMinimumVSVersion="$MinimumVSVersion" ` + -dSDKProductBandVersion="$SDKProductBandVersion" ` + -dNugetVersion="$DotnetCLINugetVersion" ` + -dVersionMajor="$VersionMajor" ` + -dVersionMinor="$VersionMinor" ` + -dCLISDKMsiSourcePath="$CLISDKMSIFile" ` + -dDependencyKeyName="$DependencyKeyName" ` + -dUpgradeCode="$UpgradeCode" ` + -dSharedFXMsiSourcePath="$SharedFxMSIFile" ` + -dHostFXRMsiSourcePath="$HostFxrMSIFile" ` + -dSharedHostMsiSourcePath="$SharedHostMSIFile" ` + -dWinFormsAndWpfMsiSourcePath="$WinFormsAndWpfMSIFile" ` + -dNetCoreAppTargetingPackMsiSourcePath="$NetCoreAppTargetingPackMSIFile" ` + -dNetCoreAppHostPackMsiSourcePath="$NetCoreAppHostPackMSIFile" ` + -dAlternateNetCoreAppHostPackMsiSourcePath="$AlternateNetCoreAppHostPackMSIFile" ` + -dArm64NetCoreAppHostPackMsiSourcePath="$Arm64NetCoreAppHostPackMSIFile" ` + -dNetStandardTargetingPackMsiSourcePath="$NetStandardTargetingPackMSIFile" ` + -dAspNetTargetingPackMsiSourcePath="$AspNetTargetingPackMSIFile" ` + -dWindowsDesktopTargetingPackMsiSourcePath="$WindowsDesktopTargetingPackMSIFile" ` + -dFinalizerExeSourcePath="$FinalizerExe" ` + -dTemplatesMsiSourcePath="$TemplatesMSIFile" ` + -dManifestsMsiSourcePath="$ManifestsMSIFile" ` + -dWinFormsAndWpfVersion="$WindowsDesktopVersion" ` + -dAdditionalSharedFXMsiSourcePath="$AdditionalSharedFxMSIFile" ` + -dAdditionalHostFXRMsiSourcePath="$AdditionalHostFxrMSIFile" ` + -dAdditionalSharedHostMsiSourcePath="$AdditionalSharedHostMSIFile" ` + -dDotNetRuntimeVersion="$DotNetRuntimeVersion" ` + -dAspNetCoreVersion="$AspNetCoreVersion" ` + -dLocalizedContentDirs="$LocalizedContentDirs" ` + -arch "$Architecture" ` + -ext WixBalExtension.dll ` + -ext WixUtilExtension.dll ` + -ext WixTagExtension.dll ` + "$AuthWsxRoot\bundle.wxs" "$WorkloadManifestWxsFile" + + Write-Information "Candle output: $candleOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Candle failed with exit code $LastExitCode." + } + + popd + return $result +} + +function RunLightForBundle +{ + $result = $true + pushd "$WixRoot" + + $WorkloadManifestWixobjFile = [System.IO.Path]::GetFileNameWithoutExtension($WorkloadManifestWxsFile) + ".wixobj" + + Write-Information "Running light for bundle.." + + $lightOutput = .\light.exe -nologo ` + -cultures:en-us ` + bundle.wixobj ` + $WorkloadManifestWixobjFile ` + $ASPNETRuntimeWixlibFile ` + -ext WixBalExtension.dll ` + -ext WixUtilExtension.dll ` + -ext WixTagExtension.dll ` + -b "$AuthWsxRoot" ` + -out $DotnetBundleOutput + + Write-Information "Light output: $lightOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Light failed with exit code $LastExitCode." + } + + popd + return $result +} + + +if(!(Test-Path $CLISDKMSIFile)) +{ + throw "$CLISDKMSIFile not found" +} + +if(!(Test-Path $ASPNETRuntimeWixLibFile)) +{ + throw "$ASPNETRuntimeWixLibFile not found" +} + +if([string]::IsNullOrEmpty($WixRoot)) +{ + Exit -1 +} + +Write-Information "Creating dotnet Bundle at $DotnetBundleOutput" + +$AuthWsxRoot = $PSScriptRoot +$LocalizedContentDirs = (Get-ChildItem "$AuthWsxRoot\LCID\*\bundle.wxl").Directory.Name -join ';' + +if(-Not (RunCandleForBundle)) +{ + Exit -1 +} + +if(-Not (RunLightForBundle)) +{ + Exit -1 +} + +if(!(Test-Path $DotnetBundleOutput)) +{ + throw "Unable to create the dotnet bundle." + Exit -1 +} + +Write-Information "Successfully created dotnet bundle - $DotnetBundleOutput" + +exit $LastExitCode diff --git a/src/redist/packaging/windows/generatemsi.ps1 b/src/redist/packaging/windows/generatemsi.ps1 new file mode 100644 index 000000000..126f42804 --- /dev/null +++ b/src/redist/packaging/windows/generatemsi.ps1 @@ -0,0 +1,168 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +param( + [Parameter(Mandatory=$true)][string]$inputDir, + [Parameter(Mandatory=$true)][string]$DotnetMSIOutput, + [Parameter(Mandatory=$true)][string]$WixRoot, + [Parameter(Mandatory=$true)][string]$ProductMoniker, + [Parameter(Mandatory=$true)][string]$SdkFeatureBandVersion, + [Parameter(Mandatory=$true)][string]$DotnetMSIVersion, + [Parameter(Mandatory=$true)][string]$SDKBundleVersion, + [Parameter(Mandatory=$true)][string]$DotnetCLINugetVersion, + [Parameter(Mandatory=$true)][string]$VersionMajor, + [Parameter(Mandatory=$true)][string]$VersionMinor, + [Parameter(Mandatory=$true)][string]$UpgradeCode, + [Parameter(Mandatory=$true)][string]$DependencyKeyName, + [Parameter(Mandatory=$true)][string]$Architecture, + [Parameter(Mandatory=$true)][string]$StableFileIdForApphostTransform +) + +$InstallFileswsx = ".\install-files.wxs" +$InstallFilesWixobj = "install-files.wixobj" + +function RunHeat +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running heat.." + + # -t $StableFileIdForApphostTransform to avoid sign check baseline apphost.exe name changes every build. Sign check uses File Id in MSI as exception list name. + # Template apphost.exe get a new "File Id" in msi different every time (since File Id is generated according to file + # path, and file path has version number) + # use XSLT transform to match the file path contains "AppHostTemplate\apphost.exe" and give it the same ID all the time. + + $heatOutput = .\heat.exe dir `"$inputDir`" -template fragment ` + -sreg -ag ` + -var var.DotnetSrc ` + -cg InstallFiles ` + -srd ` + -dr DOTNETHOME ` + -t $StableFileIdForApphostTransform ` + -out install-files.wxs + + Write-Information "Heat output: $heatOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Heat failed with exit code $LastExitCode." + } + + popd + Write-Information "RunHeat result: $result" + return $result +} + +function RunCandle +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running candle.." + + $candleOutput = .\candle.exe -nologo ` + -dDotnetSrc="$inputDir" ` + -dMicrosoftEula="$PSScriptRoot\dummyeula.rtf" ` + -dProductMoniker="$ProductMoniker" ` + -dBuildVersion="$DotnetMSIVersion" ` + -dSdkFeatureBandVersion="$SdkFeatureBandVersion" ` + -dSDKBundleVersion="$SDKBundleVersion" ` + -dNugetVersion="$DotnetCLINugetVersion" ` + -dVersionMajor="$VersionMajor" ` + -dVersionMinor="$VersionMinor" ` + -dUpgradeCode="$UpgradeCode" ` + -dDependencyKeyName="$DependencyKeyName" ` + -arch "$Architecture" ` + -ext WixDependencyExtension.dll ` + "$PSScriptRoot\dotnet.wxs" ` + "$PSScriptRoot\dotnethome_x64.wxs" ` + "$PSScriptRoot\provider.wxs" ` + "$PSScriptRoot\registrykeys.wxs" ` + $InstallFileswsx + + Write-Information "Candle output: $candleOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Candle failed with exit code $LastExitCode." + } + + popd + return $result +} + +function RunLight +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running light.." + $CabCache = Join-Path $WixRoot "cabcache" + + $lightOutput = .\light.exe -nologo -ext WixUIExtension -ext WixDependencyExtension -ext WixUtilExtension ` + -cultures:en-us ` + dotnet.wixobj ` + dotnethome_x64.wixobj ` + provider.wixobj ` + registrykeys.wixobj ` + $InstallFilesWixobj ` + -b "$inputDir" ` + -b "$PSScriptRoot" ` + -reusecab ` + -cc "$CabCache" ` + -out $DotnetMSIOutput + + Write-Information "Light output: $lightOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Light failed with exit code $LastExitCode." + } + + popd + return $result +} + +if(!(Test-Path $inputDir)) +{ + throw "$inputDir not found" +} + +Write-Information "Creating dotnet MSI at $DotnetMSIOutput" + +if([string]::IsNullOrEmpty($WixRoot)) +{ + Exit -1 +} + +if(-Not (RunHeat)) +{ + Write-Information "Heat failed" + Exit -1 +} + +if(-Not (RunCandle)) +{ + Write-Information "Candle failed" + Exit -1 +} + +if(-Not (RunLight)) +{ + Write-Information "Light failed" + Exit -1 +} + +if(!(Test-Path $DotnetMSIOutput)) +{ + throw "Unable to create the dotnet msi." + Exit -1 +} + +Write-Information "Successfully created dotnet MSI - $DotnetMSIOutput" + +exit $LastExitCode diff --git a/src/redist/packaging/windows/generatenupkg.ps1 b/src/redist/packaging/windows/generatenupkg.ps1 new file mode 100644 index 000000000..e52b9d11e --- /dev/null +++ b/src/redist/packaging/windows/generatenupkg.ps1 @@ -0,0 +1,44 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +param( + [Parameter(Mandatory=$true)][string]$BinDir, + [Parameter(Mandatory=$true)][string]$ContentPath, + [Parameter(Mandatory=$true)][string]$NugetVersion, + [Parameter(Mandatory=$true)][string]$NuspecFile, + [Parameter(Mandatory=$true)][string]$NupkgFile, + [Parameter(Mandatory=$false)][string]$Architecture, + [Parameter(Mandatory=$false)][string]$MmVersion +) + +[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bOR [Net.SecurityProtocolType]::Tls12 + +$NuGetDir = Join-Path $BinDir "nuget" +$NuGetExe = Join-Path $NuGetDir "nuget.exe" +$OutputDirectory = [System.IO.Path]::GetDirectoryName($NupkgFile) +$ContentPath = [System.IO.Path]::GetFullPath($ContentPath) +if ($CabPath) { + $CabPath = [System.IO.Path]::GetFullPath($CabPath) +} + +if (-not (Test-Path $NuGetDir)) { + New-Item -ItemType Directory -Force -Path $NuGetDir | Out-Null +} + +if (-not (Test-Path $NuGetExe)) { + # Using 3.5.0 to workaround https://github.com/NuGet/Home/issues/5016 + Write-Output "Downloading nuget.exe to $NuGetExe" + wget https://dist.nuget.org/win-x86-commandline/v3.5.0/nuget.exe -OutFile $NuGetExe +} + +if (-not (Test-Path $NuGetExe)) { + Write-Error "Could not download nuget.exe" + Exit 1 +} + +if (Test-Path $NupkgFile) { + Remove-Item -Force $NupkgFile +} + +& $NuGetExe pack $NuspecFile -Version $NugetVersion -OutputDirectory $OutputDirectory -NoDefaultExcludes -NoPackageAnalysis -Properties PAYLOAD_FILES=$ContentPath`;ARCH=$Architecture`;MAJOR_MINOR=$MmVersion +Exit $LastExitCode diff --git a/src/redist/packaging/windows/generatesdkplaceholdermsi.ps1 b/src/redist/packaging/windows/generatesdkplaceholdermsi.ps1 new file mode 100644 index 000000000..a4f167458 --- /dev/null +++ b/src/redist/packaging/windows/generatesdkplaceholdermsi.ps1 @@ -0,0 +1,109 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +param( + [Parameter(Mandatory=$true)][string]$DotnetMSIOutput, + [Parameter(Mandatory=$true)][string]$WixRoot, + [Parameter(Mandatory=$true)][string]$ProductMoniker, + [Parameter(Mandatory=$true)][string]$DotnetMSIVersion, + [Parameter(Mandatory=$true)][string]$SDKBundleVersion, + [Parameter(Mandatory=$true)][string]$DotnetCLINugetVersion, + [Parameter(Mandatory=$true)][string]$VersionMajor, + [Parameter(Mandatory=$true)][string]$VersionMinor, + [Parameter(Mandatory=$true)][string]$UpgradeCode, + [Parameter(Mandatory=$true)][string]$DependencyKeyName, + [Parameter(Mandatory=$true)][string]$Architecture +) + +function RunCandle +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running candle.." + + $candleOutput = .\candle.exe -nologo ` + -dDotnetSrc="$inputDir" ` + -dMicrosoftEula="$PSScriptRoot\dummyeula.rtf" ` + -dProductMoniker="$ProductMoniker" ` + -dBuildVersion="$DotnetMSIVersion" ` + -dSDKBundleVersion="$SDKBundleVersion" ` + -dNugetVersion="$DotnetCLINugetVersion" ` + -dVersionMajor="$VersionMajor" ` + -dVersionMinor="$VersionMinor" ` + -dUpgradeCode="$UpgradeCode" ` + -dDependencyKeyName="$DependencyKeyName" ` + -arch "$Architecture" ` + -ext WixDependencyExtension.dll ` + "$PSScriptRoot\sdkplaceholder.wxs" ` + "$PSScriptRoot\provider.wxs" + + Write-Information "Candle output: $candleOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Candle failed with exit code $LastExitCode." + } + + popd + return $result +} + +function RunLight +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running light.." + $CabCache = Join-Path $WixRoot "cabcache" + + $lightOutput = .\light.exe -nologo -ext WixUIExtension -ext WixDependencyExtension -ext WixUtilExtension ` + -cultures:en-us ` + sdkplaceholder.wixobj ` + provider.wixobj ` + -b "$PSScriptRoot" ` + -reusecab ` + -cc "$CabCache" ` + -out $DotnetMSIOutput + + Write-Information "Light output: $lightOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Light failed with exit code $LastExitCode." + } + + popd + return $result +} + +Write-Information "Creating SdkPlaceholder MSI at $DotnetMSIOutput" + +if([string]::IsNullOrEmpty($WixRoot)) +{ + Exit -1 +} + +if(-Not (RunCandle)) +{ + Write-Information "Candle failed" + Exit -1 +} + +if(-Not (RunLight)) +{ + Write-Information "Light failed" + Exit -1 +} + +if(!(Test-Path $DotnetMSIOutput)) +{ + throw "Unable to create the SdkPlaceholder MSI." + Exit -1 +} + +Write-Information "Successfully created SdkPlaceholder MSI - $DotnetMSIOutput" + +exit $LastExitCode diff --git a/src/redist/packaging/windows/generatetemplatesmsi.ps1 b/src/redist/packaging/windows/generatetemplatesmsi.ps1 new file mode 100644 index 000000000..2858c5913 --- /dev/null +++ b/src/redist/packaging/windows/generatetemplatesmsi.ps1 @@ -0,0 +1,157 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +param( + [Parameter(Mandatory=$true)][string]$inputDir, + [Parameter(Mandatory=$true)][string]$DotnetMSIOutput, + [Parameter(Mandatory=$true)][string]$WixRoot, + [Parameter(Mandatory=$true)][string]$ProductMoniker, + [Parameter(Mandatory=$true)][string]$DotnetMSIVersion, + [Parameter(Mandatory=$true)][string]$SDKBundleVersion, + [Parameter(Mandatory=$true)][string]$DotnetCLINugetVersion, + [Parameter(Mandatory=$true)][string]$VersionMajor, + [Parameter(Mandatory=$true)][string]$VersionMinor, + [Parameter(Mandatory=$true)][string]$UpgradeCode, + [Parameter(Mandatory=$true)][string]$DependencyKeyName, + [Parameter(Mandatory=$true)][string]$Architecture +) + +$InstallFileswsx = ".\template-install-files.wxs" +$InstallFilesWixobj = "template-install-files.wixobj" + +function RunHeat +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running heat.." + + $heatOutput = .\heat.exe dir `"$inputDir`" -template fragment ` + -sreg -ag ` + -var var.DotnetSrc ` + -cg InstallFiles ` + -srd ` + -dr DOTNETHOME ` + -out template-install-files.wxs + + Write-Information "Heat output: $heatOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Heat failed with exit code $LastExitCode." + } + + popd + Write-Information "RunHeat result: $result" + return $result +} + +function RunCandle +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running candle.." + + $candleOutput = .\candle.exe -nologo ` + -dDotnetSrc="$inputDir" ` + -dMicrosoftEula="$PSScriptRoot\dummyeula.rtf" ` + -dProductMoniker="$ProductMoniker" ` + -dBuildVersion="$DotnetMSIVersion" ` + -dSDKBundleVersion="$SDKBundleVersion" ` + -dNugetVersion="$DotnetCLINugetVersion" ` + -dVersionMajor="$VersionMajor" ` + -dVersionMinor="$VersionMinor" ` + -dUpgradeCode="$UpgradeCode" ` + -dDependencyKeyName="$DependencyKeyName" ` + -arch "$Architecture" ` + -ext WixDependencyExtension.dll ` + "$PSScriptRoot\dotnethome_x64.wxs" ` + "$PSScriptRoot\templates.wxs" ` + "$PSScriptRoot\provider.wxs" ` + $InstallFileswsx + + Write-Information "Candle output: $candleOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Candle failed with exit code $LastExitCode." + } + + popd + return $result +} + +function RunLight +{ + $result = $true + pushd "$WixRoot" + + Write-Information "Running light.." + $CabCache = Join-Path $WixRoot "cabcache" + + $lightOutput = .\light.exe -nologo -ext WixUIExtension -ext WixDependencyExtension -ext WixUtilExtension ` + -cultures:en-us ` + dotnethome_x64.wixobj ` + templates.wixobj ` + provider.wixobj ` + $InstallFilesWixobj ` + -b "$inputDir" ` + -b "$PSScriptRoot" ` + -reusecab ` + -cc "$CabCache" ` + -out $DotnetMSIOutput + + Write-Information "Light output: $lightOutput" + + if($LastExitCode -ne 0) + { + $result = $false + Write-Information "Light failed with exit code $LastExitCode." + } + + popd + return $result +} + +if(!(Test-Path $inputDir)) +{ + throw "$inputDir not found" +} + +Write-Information "Creating templates MSI at $DotnetMSIOutput" + +if([string]::IsNullOrEmpty($WixRoot)) +{ + Exit -1 +} + +if(-Not (RunHeat)) +{ + Write-Information "Heat failed" + Exit -1 +} + +if(-Not (RunCandle)) +{ + Write-Information "Candle failed" + Exit -1 +} + +if(-Not (RunLight)) +{ + Write-Information "Light failed" + Exit -1 +} + +if(!(Test-Path $DotnetMSIOutput)) +{ + throw "Unable to create the templates MSI." + Exit -1 +} + +Write-Information "Successfully created templates MSI - $DotnetMSIOutput" + +exit $LastExitCode diff --git a/src/redist/packaging/windows/manifests.wxs b/src/redist/packaging/windows/manifests.wxs new file mode 100644 index 000000000..721555180 --- /dev/null +++ b/src/redist/packaging/windows/manifests.wxs @@ -0,0 +1,39 @@ + + + + + + + Installed OR ALLOWMSIINSTALL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/packaging/windows/msi b/src/redist/packaging/windows/msi new file mode 100644 index 000000000..e69de29bb diff --git a/src/redist/packaging/windows/msi.ico b/src/redist/packaging/windows/msi.ico new file mode 100644 index 000000000..c2e2717c2 Binary files /dev/null and b/src/redist/packaging/windows/msi.ico differ diff --git a/src/redist/packaging/windows/provider.wxs b/src/redist/packaging/windows/provider.wxs new file mode 100644 index 000000000..84c8677d0 --- /dev/null +++ b/src/redist/packaging/windows/provider.wxs @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/redist/packaging/windows/registrykeys.wxs b/src/redist/packaging/windows/registrykeys.wxs new file mode 100644 index 000000000..32536a7d5 --- /dev/null +++ b/src/redist/packaging/windows/registrykeys.wxs @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/packaging/windows/sdkplaceholder.wxs b/src/redist/packaging/windows/sdkplaceholder.wxs new file mode 100644 index 000000000..50974bc1b --- /dev/null +++ b/src/redist/packaging/windows/sdkplaceholder.wxs @@ -0,0 +1,35 @@ + + + + + + + Installed OR ALLOWMSIINSTALL + + + + NOT Installed OR ALLOWMSIUNINSTALL + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/packaging/windows/templates.wxs b/src/redist/packaging/windows/templates.wxs new file mode 100644 index 000000000..721555180 --- /dev/null +++ b/src/redist/packaging/windows/templates.wxs @@ -0,0 +1,39 @@ + + + + + + + Installed OR ALLOWMSIINSTALL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/packaging/windows/variables.wxi b/src/redist/packaging/windows/variables.wxi new file mode 100644 index 000000000..da37e6030 --- /dev/null +++ b/src/redist/packaging/windows/variables.wxi @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/projects/DownloadPackage.csproj b/src/redist/projects/DownloadPackage.csproj new file mode 100644 index 000000000..c918ab88b --- /dev/null +++ b/src/redist/projects/DownloadPackage.csproj @@ -0,0 +1,25 @@ + + + + + false + true + true + + + + + + + + + + + + + diff --git a/src/redist/projects/SdkResolver.csproj b/src/redist/projects/SdkResolver.csproj new file mode 100644 index 000000000..b445480d8 --- /dev/null +++ b/src/redist/projects/SdkResolver.csproj @@ -0,0 +1,35 @@ + + + + $(CoreSdkTargetFramework) + false + false + false + false + + + + + + + + + + + + + + + + + <_SdkResolverSrc Include="$(PkgMicrosoft_NETCore_App_Runtime_win-x86)\runtimes\win-x86\native\hostfxr.dll" Arch="x86\" /> + <_SdkResolverSrc Include="$(PkgMicrosoft_NETCore_App_Runtime_win-x64)\runtimes\win-x64\native\hostfxr.dll" Arch="x64\" /> + <_SdkResolverSrc Include="$(PkgMicrosoft_NETCore_App_Runtime_win-arm64)\runtimes\win-arm64\native\hostfxr.dll" Arch="arm64\" /> + <_SdkResolverSrc Include="$(PkgMicrosoft_DotNet_MSBuildSdkResolver)\lib\net472\**\*.dll" Arch="" /> + <_SdkResolverDst Include="@(_SdkResolverSrc->'$(OutputPath)%(Arch)%(RecursiveDir)%(Filename)%(Extension)')" /> + + + + + + diff --git a/src/redist/projects/VSTemplateLocator.csproj b/src/redist/projects/VSTemplateLocator.csproj new file mode 100644 index 000000000..79127dfa1 --- /dev/null +++ b/src/redist/projects/VSTemplateLocator.csproj @@ -0,0 +1,37 @@ + + + + $(CoreSdkTargetFramework) + false + false + false + false + + + + + + + + + + + + + + + + + + <_VSTemplateLocatorSrc Include="$(PkgMicrosoft_NETCore_App_Runtime_win-x86)\runtimes\win-x86\native\hostfxr.dll" Arch="x86\" /> + <_VSTemplateLocatorSrc Include="$(PkgMicrosoft_NETCore_App_Runtime_win-x64)\runtimes\win-x64\native\hostfxr.dll" Arch="x64\" /> + <_VSTemplateLocatorSrc Include="$(PkgMicrosoft_NETCore_App_Runtime_win-arm64)\runtimes\win-arm64\native\hostfxr.dll" Arch="arm64\" /> + <_VSTemplateLocatorSrc Include="$(Pkgmicrosoft_dotnet_templateLocator)\lib\net472\**\*.dll" Arch="" /> + <_VSTemplateLocatorSrc Include="$(PkgMicrosoft_Deployment_DotNet_Releases)\lib\net452\**\*.dll" Arch="" /> + <_VSTemplateLocatorDst Include="@(_VSTemplateLocatorSrc->'$(OutputPath)VSTemplateLocator\%(Arch)%(RecursiveDir)%(Filename)%(Extension)')" /> + + + + + + diff --git a/src/redist/redist.csproj b/src/redist/redist.csproj new file mode 100644 index 000000000..816123d51 --- /dev/null +++ b/src/redist/redist.csproj @@ -0,0 +1,44 @@ + + + + $(CoreSdkTargetFramework) + true + false + false + true + none + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/Badge.targets b/src/redist/targets/Badge.targets new file mode 100644 index 000000000..ee8bc27b3 --- /dev/null +++ b/src/redist/targets/Badge.targets @@ -0,0 +1,33 @@ + + + + $(RepoRoot)/resources/images/version_badge.svg + + + + + + + + + + + $(OSName)_$(Architecture) + rhel.6_x64 + linux_musl_x64 + linux_$(Architecture) + all_linux_distros_native_installer + + $(ArtifactsShippingPackagesDir)$(VersionBadgeMoniker)_$(Configuration)_version_badge.svg + $(ArtifactsShippingPackagesDir)$(VersionBadgeMoniker)_$(Configuration)_coherent_badge.svg + + + + diff --git a/src/redist/targets/Branding.targets b/src/redist/targets/Branding.targets new file mode 100644 index 000000000..be143b87a --- /dev/null +++ b/src/redist/targets/Branding.targets @@ -0,0 +1,20 @@ + + + + + cli + Microsoft .NET SDK $(Version) + Microsoft .NET Toolset $(Version) + .NET Standard Support for Visual Studio 2015 + Microsoft .NET Runtime $(MicrosoftNETCoreAppRuntimePackageVersion) + Microsoft .NET Targeting Pack $(MicrosoftNETCoreAppRefPackageVersion) + Microsoft .NET Standard 2.1 Targeting Pack $(NETStandardLibraryRefPackageVersion) + Microsoft .NET AppHost Pack $(MicrosoftNETCoreAppHostHostPackageVersion) + Microsoft .NET Host $(SharedHostVersion) + Microsoft .NET Host FX Resolver $(HostFxrVersion) + Microsoft.NETCore.App + $(SharedFrameworkName) + + + + diff --git a/src/redist/targets/BuildCoreSdkTasks.targets b/src/redist/targets/BuildCoreSdkTasks.targets new file mode 100644 index 000000000..ff3cf1bef --- /dev/null +++ b/src/redist/targets/BuildCoreSdkTasks.targets @@ -0,0 +1,46 @@ + + + + + $(CoreSdkTargetFramework) + net472 + + $(ArtifactsDir)tasks\bin\core-sdk-tasks\$(Configuration)\$(TaskTargetFramework)\core-sdk-tasks.dll + $(RepoRoot)src\core-sdk-tasks\core-sdk-tasks.csproj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/BundledDotnetTools.targets b/src/redist/targets/BundledDotnetTools.targets new file mode 100644 index 000000000..a09b18ec1 --- /dev/null +++ b/src/redist/targets/BundledDotnetTools.targets @@ -0,0 +1,43 @@ + + + + + + + + + + + %(Identity)/%(Version)/ + $(NuGetPackageRoot)$([MSBuild]::ValueOrDefault('%(PackagePathRelativeToPackageRoot)', '').ToLower())/ + %(Identity)/%(Version)/%(Identity).%(Version).nupkg + $(NuGetPackageRoot)$([MSBuild]::ValueOrDefault('%(NupkgPathRelativeToPackageRoot)', '').ToLower()) + + + + + + + + + PackageToRestore=%(BundledDotnetTool.Identity); + PackageVersionToRestore=%(BundledDotnetTool.Version); + TargetFramework=$(TargetFramework); + RestoreProjectStyle=DotnetToolReference + + + + + + + + + + + + + + + diff --git a/src/redist/targets/BundledManifests.targets b/src/redist/targets/BundledManifests.targets new file mode 100644 index 000000000..039f9a1fc --- /dev/null +++ b/src/redist/targets/BundledManifests.targets @@ -0,0 +1,128 @@ + + + + 8.0.100 + + + + + + + + + + + + + + + + + + + + + + + + + + $(Architecture) + + + + %(Identity).Manifest-%(FeatureBand) + $(NuGetPackageRoot)$([MSBuild]::ValueOrDefault('%(NupkgId)', '').ToLower())/$([MSBuild]::ValueOrDefault('%(Version)', '').ToLower()) + %(Identity).Manifest-%(FeatureBand).Msi.$(MsiArchitectureForWorkloadManifests) + $(NuGetPackageRoot)$([MSBuild]::ValueOrDefault('%(MsiNupkgId)', '').ToLower())/$([MSBuild]::ValueOrDefault('%(Version)', '').ToLower()) + + + + + + + [%(Version)] + + + + + + [%(Version)] + + + + + + + + $(PkgMicrosoft_DotNet_SignCheck)\tools\Microsoft.DotNet.SignCheck.exe + $(ArtifactsLogDir)\workloadmanifestsigncheck.log + $(ArtifactsLogDir)\workloadmanifestsigncheck.errors.log + + + + + + $([MSBuild]::NormalizePath($([System.IO.Directory]::GetFiles('%(RestoredMsiNupkgContentPath)/data/', '*$(MsiArchitectureForWorkloadManifests).msi')))) + + + + + + + + + + + + + + + + + + + + + + + + + $(VersionPrefix)-baseline$(_BuildNumberLabels) + $(Version) + $(RedistLayoutPath)sdk-manifests\$(NetFeatureBand)\workloadsets\$(WorkloadSetVersion) + + + + + + + + + + + + + + + diff --git a/src/redist/targets/BundledTemplates.targets b/src/redist/targets/BundledTemplates.targets new file mode 100644 index 000000000..c0f67ccc2 --- /dev/null +++ b/src/redist/targets/BundledTemplates.targets @@ -0,0 +1,133 @@ + + + + + + + + + + + $(ArtifactsNonShippingPackagesDir)%(TemplatesComponents.TemplateBaseFilename)-$(FullNugetVersion)-$(ProductMonikerRid)$(InstallerExtension) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %(Identity)/%(PackageVersion)/%(Identity).%(PackageVersion).nupkg + $(NuGetPackageRoot)$([MSBuild]::ValueOrDefault('%(NupkgPathRelativeToPackageRoot)', '').ToLower()) + + + + + + + + + + + + + $(TargetFramework) + + + @(BundledTemplatePackageDownload->'', ' + ') + + +]]> + + + $(TemplatePackageDownloadProjectContent.Replace(';', '%3B')) + $(IntermediateOutputPath)TemplatePackageDownloader\ + $(TemplatePackageDownloadProjectDirectory)TemplatePackageDownloader.csproj + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/redist/targets/Checksum.targets b/src/redist/targets/Checksum.targets new file mode 100644 index 000000000..82c0db2ca --- /dev/null +++ b/src/redist/targets/Checksum.targets @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + %(ArtifactsForGeneratingChecksums.Identity).sha + + + + + diff --git a/src/redist/targets/Crossgen.targets b/src/redist/targets/Crossgen.targets new file mode 100644 index 000000000..7227f238e --- /dev/null +++ b/src/redist/targets/Crossgen.targets @@ -0,0 +1,210 @@ + + + + + + microsoft.netcore.app.runtime.$(SharedFrameworkRid) + microsoft.netcore.app.crossgen2.$(Crossgen2Rid) + $(NuGetPackageRoot)$(RuntimeNETCrossgenPackageName)/$(MicrosoftNETCoreAppRuntimePackageVersion)/tools/crossgen2$(ExeExtension) + + $(RedistLayoutPath)shared/$(SharedFrameworkName)/$(MicrosoftNETCoreAppRuntimePackageVersion) + $(RedistLayoutPath)shared/$(SharedFrameworkName)/$(VSRedistCommonNetCoreTargetingPackx6490PackageVersion) + + + + + + + PackageToRestore=$(RuntimeNETCrossgenPackageName); + PackageVersionToRestore=$(MicrosoftNETCoreAppRuntimePackageVersion); + TargetFramework=$(TargetFramework) + + + + + + + + + net9.0 + + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk\tools\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk.BlazorWebAssembly\tools\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Sdks\NuGet.Build.Tasks.Pack\CoreCLR\ + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk.Razor\tasks\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk.WindowsDesktop\tools\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk.Publish\tools\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk.Web\tools\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk.Web.ProjectSystem\tools\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Sdks\Microsoft.NET.Sdk.Worker\tools\$(DefaultToolTfm)\ + $(SdkOutputDirectory)Containers\tasks\$(DefaultToolTfm)\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/FileExtensions.targets b/src/redist/targets/FileExtensions.targets new file mode 100644 index 000000000..bdbbd62af --- /dev/null +++ b/src/redist/targets/FileExtensions.targets @@ -0,0 +1,33 @@ + + + + + .tar.gz + .zip + + .msi + .pkg + .deb + .rpm + + .exe + $(InstallerExtension) + $(InstallerExtension) + $(InstallerExtension) + + lib + + + .so + .dll + .dylib + + .exe + + + .sh + .ps1 + + + + diff --git a/src/redist/targets/GenerateArchives.targets b/src/redist/targets/GenerateArchives.targets new file mode 100644 index 000000000..fac4b4b9b --- /dev/null +++ b/src/redist/targets/GenerateArchives.targets @@ -0,0 +1,32 @@ + + + + + + false + true + + + + + + + + + + + + + diff --git a/src/redist/targets/GenerateBundledVersions.targets b/src/redist/targets/GenerateBundledVersions.targets new file mode 100644 index 000000000..ac8e4ac26 --- /dev/null +++ b/src/redist/targets/GenerateBundledVersions.targets @@ -0,0 +1,1321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {%0A@(Line->' "%(Identity)": { "commit": "%(Commit)", "version": "%(Version)" }', ',%0A')%0A} + + + + + + + Microsoft.NETCoreSdk.BundledVersions.props + + + + <_NETCoreAppPackageVersion>$(MicrosoftNETCoreAppRuntimePackageVersion) + <_NETStandardLibraryPackageVersion>$(NETStandardLibraryRefPackageVersion) + <_NETCorePlatformsPackageVersion>$(MicrosoftNETCorePlatformsPackageVersion) + + <_NET80RuntimePackVersion>8.0.$(VersionFeature80) + <_NET80TargetingPackVersion>8.0.$(VersionFeature80) + <_WindowsDesktop80RuntimePackVersion>8.0.$(VersionFeature80) + <_WindowsDesktop80TargetingPackVersion>8.0.$(VersionFeature80) + <_AspNet80RuntimePackVersion>8.0.$(VersionFeature80) + <_AspNet80TargetingPackVersion>8.0.$(VersionFeature80) + + <_NET70RuntimePackVersion>7.0.$(VersionFeature70) + <_NET70TargetingPackVersion>7.0.$(VersionFeature70) + <_WindowsDesktop70RuntimePackVersion>7.0.$(VersionFeature70) + <_WindowsDesktop70TargetingPackVersion>7.0.$(VersionFeature70) + <_AspNet70RuntimePackVersion>7.0.$(VersionFeature70) + <_AspNet70TargetingPackVersion>7.0.$(VersionFeature70) + + <_NET60RuntimePackVersion>6.0.$(VersionFeature60) + <_NET60TargetingPackVersion>6.0.$(VersionFeature60) + <_WindowsDesktop60RuntimePackVersion>6.0.$(VersionFeature60) + <_WindowsDesktop60TargetingPackVersion>6.0.$(VersionFeature60) + <_AspNet60RuntimePackVersion>6.0.$(VersionFeature60) + <_AspNet60TargetingPackVersion>6.0.$(VersionFeature60) + + <_NET50RuntimePackVersion>5.0.$(VersionFeature50) + <_NET50TargetingPackVersion>5.0.0 + <_WindowsDesktop50RuntimePackVersion>5.0.$(VersionFeature50) + <_WindowsDesktop50TargetingPackVersion>5.0.0 + <_AspNet50RuntimePackVersion>5.0.$(VersionFeature50) + <_AspNet50TargetingPackVersion>5.0.0 + + <_NETCoreApp30RuntimePackVersion>3.0.3 + <_NETCoreApp30TargetingPackVersion>3.0.0 + + <_NETCoreApp31RuntimePackVersion>3.1.$(VersionFeature31) + <_NETCoreApp31TargetingPackVersion>3.1.0 + + <_WindowsDesktop30RuntimePackVersion>3.0.3 + <_WindowsDesktop30TargetingPackVersion>3.0.0 + + <_WindowsDesktop31RuntimePackVersion>3.1.$(VersionFeature31) + <_WindowsDesktop31TargetingPackVersion>3.1.0 + + <_AspNet30RuntimePackVersion>3.0.3 + <_AspNet30TargetingPackVersion>3.0.1 + + <_AspNet31RuntimePackVersion>3.1.$(VersionFeature31) + <_AspNet31TargetingPackVersion>3.1.10 + + + <_NETCoreAppTargetFrameworkVersion>$(_NETCoreAppPackageVersion.Split('.')[0]).$(_NETCoreAppPackageVersion.Split('.')[1]) + <_NETStandardTargetFrameworkVersion>$(_NETStandardLibraryPackageVersion.Split('.')[0]).$(_NETStandardLibraryPackageVersion.Split('.')[1]) + + <_NETCoreSdkBeingBuiltIsPreview Condition=" '$(DotNetFinalVersionKind)' != 'release' ">true + <_NETCoreSdkBeingBuiltIsPreview Condition=" '$(DotNetFinalVersionKind)' == 'release' ">false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_KnownRuntimeIdentiferPlatforms Include="any;aot;freebsd;illumos;solaris;unix" /> + <_ExcludedKnownRuntimeIdentiferPlatforms Include="rhel.6;tizen.4.0.0;tizen.5.0.0" /> + <_ExcludedKnownRuntimeIdentiferPlatforms Include="rhel.6" /> + <_ExcludedKnownRuntimeIdentiferPlatforms Include="tizen.4.0.0" /> + <_ExcludedKnownRuntimeIdentiferPlatforms Include="tizen.5.0.0" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + $(ProductMonikerRid) + + + + + + + + + %24([MSBuild]::NormalizePath('%24(MSBuildThisFileDirectory)..\..\')) + %24([MSBuild]::EnsureTrailingSlash('%24(NetCoreRoot)'))packs + + <_NetFrameworkHostedCompilersVersion>$(MicrosoftNetCompilersToolsetPackageVersion) + $(_NETCoreAppTargetFrameworkVersion) + $(_NETCoreAppTargetFrameworkVersion) + $(_NETCoreAppPackageVersion) + $(_NETStandardTargetFrameworkVersion) + $(_NETStandardLibraryPackageVersion) + $(_NETCorePlatformsPackageVersion) + %24(MSBuildThisFileDirectory)RuntimeIdentifierGraph.json + $(Version) + $(SdkFeatureBand) + $(ProductMonikerRid) + $(PortableProductMonikerRid) + <_NETCoreSdkIsPreview>$(_NETCoreSdkBeingBuiltIsPreview) + + + @(ImplicitPackageVariable->'', ' + ') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_KnownRuntimeIdentiferPlatforms Include="@(_KnownRuntimeIdentiferPlatforms, '%3B')" /> + <_ExcludedKnownRuntimeIdentiferPlatforms Include="@(_ExcludedKnownRuntimeIdentiferPlatforms, '%3B')" /> + + + +]]> + + + + + + + + + Microsoft.NETCoreSdk.BundledCliTools.props + + + + + + + + +@(BundledDotnetTools->HasMetadata('ObsoletesCliTool')->' %3CBundledDotNetCliToolReference Include="%(ObsoletesCliTool)" /%3E','%0A') + + + +]]> + + + + + + + + + $(RedistLayoutPath)sdk/$(Version)/minimumMSBuildVersion + Microsoft.NETCoreSdk.BundledMSBuildInformation.props + $(MSBuildVersion) + + + + + + + + + + <_BundledMSBuildVersionMajorMinor>$([System.Version]::Parse('$(BundledMSBuildVersion)').ToString(2)) + + + + + + + + $(MinimumMSBuildVersion) + $(BundledMSBuildVersion) + <_MSBuildVersionMajorMinor>%24([System.Version]::Parse('%24(MSBuildVersion)').ToString(2)) + <_IsDisjointMSBuildVersion>%24([MSBuild]::VersionGreaterThan('%24(_MSBuildVersionMajorMinor)', '$(_BundledMSBuildVersionMajorMinor)')) + + +]]> + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/GenerateDebs.targets b/src/redist/targets/GenerateDebs.targets new file mode 100644 index 000000000..1cccb4fd0 --- /dev/null +++ b/src/redist/targets/GenerateDebs.targets @@ -0,0 +1,446 @@ + + + + $(PkgMicrosoft_DotNet_Build_Tasks_Installers)/build/deb-package-tool + + + + + $(MSBuildProjectDirectory) + + + + + /usr/share/dotnet + + package_root + $ + samples + docs + debian + + + + + $(MicrosoftNETCoreAppMajorMinorVersion) + dotnet-runtime-deps-$(RuntimeDepsPackageVersion) + $(DownloadsFolder)$(DownloadedRuntimeDepsInstallerFileName) + $(MajorMinorVersion) + dotnet-sdk-$(SdkDebianPackageVersion) + $(MicrosoftNETCoreAppPackageVersion) + $(MicrosoftNETCoreAppMajorMinorVersion) + dotnet-runtime-$(SharedFxDebianPackageFileVersion) + $(SharedFxDebianPackageFileName.ToLower()) + dotnet-runtime-$(SharedFxDebianPackageVersion) + $(SharedFxDebianPackageName.ToLower()) + $(DownloadsFolder)$(DownloadedSharedFrameworkInstallerFileName) + dotnet-targeting-pack-$(SharedFxDebianPackageFileVersion) + $(NetCoreAppTargetingPackDebianPackageFileName.ToLower()) + dotnet-targeting-pack-$(SharedFxDebianPackageVersion) + $(NetCoreAppTargetingPackDebianPackageName.ToLower()) + $(DownloadsFolder)$(DownloadedNetCoreAppTargetingPackInstallerFileName) + netstandard-targeting-pack-$(NETStandardLibraryRefPackageVersion) + $(NetStandardTargetingPackDebianPackageFileName.ToLower()) + netstandard-targeting-pack-$(NETStandardLibraryRefPackageVersion.Split('.')[0]).$(NETStandardLibraryRefPackageVersion.Split('.')[1]) + $(NetStandardTargetingPackDebianPackageName.ToLower()) + $(DownloadsFolder)$(DownloadedNetStandardTargetingPackInstallerFileName) + dotnet-apphost-pack-$(SharedFxDebianPackageFileVersion) + $(NetCoreAppHostPackDebianPackageFileName.ToLower()) + dotnet-apphost-pack-$(SharedFxDebianPackageVersion) + $(NetCoreAppHostPackDebianPackageName.ToLower()) + $(DownloadsFolder)$(DownloadedNetCoreAppHostPackInstallerFileName) + dotnet-hostfxr-$(HostFxrVersion) + $(HostFxrDebianPackageFileName.ToLower()) + dotnet-hostfxr-$(HostFxrMajorMinorVersion) + $(HostFxrDebianPackageName.ToLower()) + $(DownloadsFolder)$(DownloadedHostFxrInstallerFileName) + dotnet-host + $(DownloadsFolder)$(DownloadedSharedHostInstallerFileName) + aspnetcore-runtime-$(MicrosoftAspNetCoreAppRuntimePackageVersion) + $(AspNetCoreSharedFxDebianPackageFileName.ToLower()) + aspnetcore-runtime-$(AspNetCoreMajorMinorVersion) + $(AspNetCoreSharedFxDebianPackageName.ToLower()) + $(DownloadsFolder)$(DownloadedAspNetCoreSharedFxInstallerFileName) + aspnetcore-targeting-pack-$(MicrosoftAspNetCoreAppRefPackageVersion) + $(AspNetTargetingPackDebianPackageFileName.ToLower()) + aspnetcore-targeting-pack-$(AspNetCoreMajorMinorVersion) + $(AspNetTargetingPackDebianPackageName.ToLower()) + $(DownloadsFolder)$(DownloadedAspNetTargetingPackInstallerFileName) + + + + + + $(RepoRoot)Documentation/manpages + $(RepoRoot)test/EndToEnd/EndToEnd.Tests.csproj + $(RedistLayoutPath)sdk/ + $(RedistLayoutPath)templates/ + + + + + + + + + + + + $(ArtifactsShippingPackagesDir) + $(InstallerOutputDirectory)$(DistroSpecificArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)$(InstallerExtension) + $(IntermediateOutputPath)debian/sdk/ + $(SdkDebianIntermediateDirectory)deb-tool-output + $(SdkDebianIntermediateDirectory)debian-testResults.xml + + + + + $(SdkDebianIntermediateDirectory)debianLayoutDirectory/ + $(LayoutDirectory)$(LayoutPackageRootDirName) + $(LayoutDirectory)$(LayoutAbsolutePlacementDirName) + $(LayoutDirectory)$(LayoutSamplesDirName) + $(LayoutDirectory)$(LayoutDocsDirName) + $(LayoutDirectory)$(LayoutDebianFilesDirName) + + + + + $(MSBuildThisFileDirectory)../packaging/deb/dotnet-debian_config.json + $(LayoutDirectory)debian_config.json + + + + $(MSBuildThisFileDirectory)../packaging/deb/postinst + $(LayoutDirectory)debian/postinst + + + + + $(SharedFxDebianPackageName) + + + $(AspNetCoreSharedFxDebianPackageName) + + + $(NetCoreAppTargetingPackDebianPackageName) + + + $(NetCoreAppHostPackDebianPackageName) + + + $(NetStandardTargetingPackDebianPackageName) + + + $(AspNetTargetingPackDebianPackageName) + + + + , + "$(AdditionalSharedFxDebianPackageName)" : {} + + + + + + + $(MajorMinorVersion) + + + $(SdkBrandName) + + + $(MicrosoftNETCoreAppRuntimePackageVersionWithTilde) + + + $(MicrosoftNETCoreAppRefPackageVersionWithTilde) + + + $(NetStandardTargetingPackPackageVersionWithTilde) + + + $(AspNetCoreRuntimeVersionWithTilde) + + + $(AspNetCoreRefVersionWithTilde) + + + $(Version) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(AspNetCoreSharedFxBaseRuntimeVersion.IndexOf('-')) + $(AspNetCoreSharedFxBaseRuntimeVersion) + $(AspNetCoreSharedFxBaseRuntimeVersion.Substring(0, $(AspNetVersionPrereleaseSeparator))) + + $(AspNetSimpleVersion.LastIndexOf('.')) + $(AspNetSimpleVersion.Substring(0, $(AspNetVersionPatchSeparator))) + + + --ignore-depends=dotnet-runtime-$(AspNetSimpleVersion) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + diff --git a/src/redist/targets/GenerateInstallers.targets b/src/redist/targets/GenerateInstallers.targets new file mode 100644 index 000000000..9f6f455e8 --- /dev/null +++ b/src/redist/targets/GenerateInstallers.targets @@ -0,0 +1,18 @@ + + + + <_BuildUnlessNoBuild Condition="'$(NoBuild)' != 'true'">Build + + + + + diff --git a/src/redist/targets/GenerateLayout.targets b/src/redist/targets/GenerateLayout.targets new file mode 100644 index 000000000..d81a17f9d --- /dev/null +++ b/src/redist/targets/GenerateLayout.targets @@ -0,0 +1,587 @@ + + + + $(BaseOutputPath)$(Configuration)\dotnet\ + $(BaseOutputPath)$(Configuration)\internal\ + $(IntermediateOutputPath)downloads\ + + + + + $(VSRedistCommonAspNetCoreSharedFrameworkx6490PackageVersion) + + $(VSRedistCommonNetCoreSharedFrameworkx6490PackageVersion) + + $(VSRedistCommonWindowsDesktopSharedFrameworkx6490PackageVersion) + + 3.0.0 + + + + + $(RedistLayoutPath)sdk\$(Version)\ + + true + https://dotnetbuilds.blob.core.windows.net/internal/ + + + https://dotnetcli.blob.core.windows.net/dotnet/ + $(OfficialBaseURL) + + $([System.Text.RegularExpressions.Regex]::Replace('$(PublicBaseURL)', '%28file:\/{1,}%29%28.+%29', 'file:///%242')) + + $(HostRid) + $(OSName)-$(Architecture) + $(HostRid.Replace('mariner.2.0', 'cm.2')) + + + -internal + + + + + $(NetRuntimeRid) + $(InstallerTargetArchitecture) + + + $(NetRuntimeRid) + $(Architecture) + $(OSName)-$(InstallerTargetArchitecture) + rhel.7-aarch64 + + x64 + x86 + dotnet-host$(InstallerStartSuffix)-$(SharedHostVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + dotnet-hostfxr$(InstallerStartSuffix)-$(HostFxrVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + dotnet-runtime$(InstallerStartSuffix)-$(MicrosoftNETCoreAppRuntimePackageVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + dotnet-runtime$(InstallerStartSuffix)$(PgoTerm)-$(VSRedistCommonNetCoreSharedFrameworkx6490PackageVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + dotnet-runtime-deps-$(SharedHostVersion)-$(RuntimeDepsInstallerFileRid)$(InstallerExtension) + windowsdesktop-runtime-$(MicrosoftWindowsDesktopAppRuntimePackageVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + dotnet-targeting-pack-$(MicrosoftNETCoreAppRefPackageVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + dotnet-apphost-pack-$(MicrosoftNETCoreAppHostPackageVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + dotnet-apphost-pack-$(MicrosoftNETCoreAppHostPackageVersion)-$(SharedFrameworkInstallerFileRid)_$(AlternateArchitecture)$(InstallerExtension) + dotnet-apphost-pack-$(MicrosoftNETCoreAppHostPackageVersion)-$(SharedFrameworkInstallerFileRid)_arm64$(InstallerExtension) + windowsdesktop-targeting-pack-$(MicrosoftWindowsDesktopAppRefPackageVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + netstandard-targeting-pack-$(NETStandardLibraryRefPackageVersion)-$(SharedFrameworkInstallerFileRid)$(InstallerExtension) + + netstandard-targeting-pack-$(NETStandardLibraryRefPackageVersion)-osx-x64$(InstallerExtension) + netstandard$(NETStandardLibraryRefPackageVersion.Split('.')[0])$(NETStandardLibraryRefPackageVersion.Split('.')[1]) + + + $(NetRuntimeRid) + $(ProductMonikerRid) + linux-$(Architecture) + dotnet-runtime-$(MicrosoftNETCoreAppRuntimePackageVersion)-$(SharedFrameworkRid)$(ArchiveExtension) + dotnet-runtime$(PgoTerm)-$(VSRedistCommonNetCoreSharedFrameworkx6490PackageVersion)-$(SharedFrameworkRid)$(ArchiveExtension) + windowsdesktop-runtime-$(MicrosoftWindowsDesktopAppRuntimePackageVersion)-$(SharedFrameworkRid)$(ArchiveExtension) + + $(HostOSName)-$(BuildArchitecture) + + $(SharedFrameworkRid) + linux-x64 + $(AspNetCoreInstallerRid) + x64 + aarch64 + + aspnetcore-runtime-$(MicrosoftAspNetCoreAppRuntimePackageVersion)-$(AspNetCoreInstallerRid)$(InstallerExtension) + aspnetcore-runtime-$(VSRedistCommonAspNetCoreSharedFrameworkx6490PackageVersion)-$(AspNetCoreInstallerRid)$(InstallerExtension) + aspnetcore-runtime-internal-$(MicrosoftAspNetCoreAppRuntimePackageVersion)-$(AspNetCoreInstallerRid).wixlib + aspnetcore-targeting-pack-$(MicrosoftAspNetCoreAppRefPackageVersion)-$(AspNetCoreInstallerRid)$(InstallerExtension) + aspnetcore-targeting-pack-$(MicrosoftAspNetCoreAppRefInternalPackageVersion)-$(AspNetCoreInstallerRid)$(InstallerExtension) + aspnetcoremodule_$(Architecture)_en_v2_$(MicrosoftAspNetCoreAppRuntimePackageVersion)$(InstallerExtension) + aspnetcore-targeting-pack-$(MicrosoftAspNetCoreAppRefPackageVersion)-$(AspNetCoreArchiveRid)$(ArchiveExtension) + aspnetcore-runtime-$(MicrosoftAspNetCoreAppRuntimePackageVersion)-$(AspNetCoreArchiveRid)$(ArchiveExtension) + + + aspnetcore_base_runtime.version + + + + + $(PublicBaseURL)Runtime/$(NetRuntimeBlobVersion) + + $(OfficialBaseURL)Runtime/$(NETStandardTargetingPackBlobVersion) + + $(PublicBaseURL)aspnetcore/Runtime/$(AspNetCoreBlobVersion) + + $(PublicBaseURL)WindowsDesktop/$(WindowsDesktopBlobVersion) + + $(PublicBaseURL)Sdk/$(MicrosoftDotnetToolsetInternalPackageVersion) + + $(IntermediateDirectory)/coreSetupDownload/$(MicrosoftNETCoreAppRuntimePackageVersion) + $(NetRuntimeDownloadDirectory)/combinedSharedHostAndFrameworkArchive$(ArchiveExtension) + + + + + + win-$(AlternateArchitecture) + win-arm64 + + + + win-x86 + win-x64 + + + + + $(NetRuntimeRootUrl) + $(CombinedFrameworkHostArchiveFileName) + + + + + Microsoft.NETCore.App.Ref + $(MicrosoftNETCoreAppRefPackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + NETStandard.Library.Ref + $(NETStandardLibraryRefPackageVersion) + $(NetStandardTargetingPackTargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + Microsoft.AspNetCore.App.Ref + $(MicrosoftAspNetCoreAppRefPackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + Microsoft.NETCore.App.Host.$(SharedFrameworkRid) + $(MicrosoftNETCoreAppHostPackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + Microsoft.NETCore.App.Host.$(AlternateAppHostRid) + $(MicrosoftNETCoreAppHostPackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + Microsoft.NETCore.App.Host.$(Arm64AppHostRid) + $(MicrosoftNETCoreAppHostPackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + Microsoft.NETCore.App.Host.$(x64AppHostRid) + $(MicrosoftNETCoreAppHostPackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + Microsoft.NETCore.App.Runtime.$(SharedFrameworkRid) + $(MicrosoftNETCoreAppRuntimePackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + Microsoft.AspNetCore.App.Runtime.$(SharedFrameworkRid) + $(MicrosoftAspNetCoreAppRuntimePackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + $(NetRuntimeRootUrl) + $(DownloadedRuntimeDepsInstallerFileName) + + + + $(NetRuntimeRootUrl) + $(DownloadedSharedFrameworkInstallerFileName) + + + + $(NetRuntimeRootUrl) + $(DownloadedSharedHostInstallerFileName) + + + + $(NetRuntimeRootUrl) + $(DownloadedHostFxrInstallerFileName) + + + + $(NetRuntimeRootUrl) + $(DownloadedNetCoreAppTargetingPackInstallerFileName) + + + + + $(NetStandardTargetingPackRootUrl) + $(DownloadedNetStandardTargetingPackInstallerFileName) + + + + $(NetRuntimeRootUrl) + $(DownloadedNetCoreAppHostPackInstallerFileName) + + + + + $(NetRuntimeRootUrl) + $(DownloadedAlternateNetCoreAppHostPackInstallerFileName) + + + + $(NetRuntimeRootUrl) + $(DownloadedArm64NetCoreAppHostPackInstallerFileName) + + + + $(WinFormsAndWpfSharedFxRootUrl) + $(DownloadedWindowsDesktopTargetingPackInstallerFileName) + + + + $(ToolsetArchiveRootUrl) + dotnet-toolset-internal-$(MicrosoftDotnetToolsetInternalPackageVersion).zip + sdk/$(Version) + + + + + + $(AspNetCoreSharedFxRootUrl) + $(AspNetCoreSharedFxArchiveFileName) + + shared/Microsoft.AspNetCore.App + + + + + $(AspNetCoreSharedFxRootUrl) + $(AspNetTargetingPackArchiveFileName) + + + + + $(AspNetCoreSharedFxRootUrl) + $(DownloadedAspNetTargetingPackInstallerFileName) + + + + $(AspNetCoreSharedFxRootUrl) + $(DownloadedAspNetCoreSharedFxInstallerFileName) + + + + $(AspNetCoreSharedFxRootUrl) + $(DownloadedAspNetCoreSharedFxWixLibFileName) + + + + $(AspNetCoreSharedFxRootUrl) + $(DownloadedAspNetCoreV2ModuleInstallerFileName) + + + + $(AspNetCoreSharedFxRootUrl) + $(AspNetCoreSharedFxBaseRuntimeVersionFileName) + + + + + false + true + false + + + + + Microsoft.WindowsDesktop.App.Ref + $(MicrosoftWindowsDesktopAppRefPackageVersion) + $(TargetFramework) + packs/%(PackageName)/%(PackageVersion) + + + + $(WinFormsAndWpfSharedFxRootUrl) + $(WinFormsAndWpfSharedFxArchiveFileName) + shared/Microsoft.WindowsDesktop.App + + + + $(WinFormsAndWpfSharedFxRootUrl) + $(DownloadedWinFormsAndWpfSharedFrameworkInstallerFileName) + + + + + + + + $(DownloadsFolder)%(DownloadFileName) + + + $(DownloadsFolder)%(DownloadFileName) + + + true + + + %(BaseUrl) + $([System.String]::new('%(ComponentToDownload.PrivateBaseUrl)').Replace('$(PublicBaseURL)', '$(InternalBaseURL)')) + + + + + + %(ComponentToDownload.ShouldDownload) + %(ComponentToDownload.DownloadDestination) + + + + + + %(ComponentToDownload.ShouldDownload) + %(ComponentToDownload.DownloadDestination) + + + %(ComponentToDownload.ShouldDownload) + %(ComponentToDownload.DownloadDestination) + $(DotNetRuntimeSourceFeedKey) + + + + + + + + + PackageToRestore=%(BundledLayoutPackage.PackageName); + PackageVersionToRestore=%(BundledLayoutPackage.PackageVersion); + TargetFramework=%(BundledLayoutPackage.TargetFramework) + + + + + + + + + + $([MSBuild]::ValueOrDefault('%(BundledLayoutPackage.PackageName)', '').ToLower()) + + + + %(BundledLayoutPackage.RelativeLayoutPath) + %(BundledLayoutPackage.Identity) + + + + + + + + %(BundledLayoutPackageDownloadFiles.RecursiveDir)%(BundledLayoutPackageDownloadFiles.Filename)%(BundledLayoutPackageDownloadFiles.Extension) + + + $(RedistLayoutPath)/%(BundledLayoutPackageDownloadFiles.RelativeLayoutPath)/%(BundledLayoutPackageDownloadFiles.DestinationPath) + + + $([MSBuild]::NormalizePath(%(BundledLayoutPackageDownloadFiles.DestinationPath))) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Microsoft.NETCore.App.Host.$(SharedFrameworkRid) + $(IntermediateOutputPath)AppHostRestore\ + $(SdkOutputDirectory)AppHostTemplate + + + + + + + + + PackageToRestore=$(NETCoreAppHostPackageName); + PackageVersionToRestore=$(MicrosoftNETCoreAppHostPackageVersion); + TargetFramework=$(TargetFramework); + RestorePackagesPath=$(AppHostRestorePath); + RuntimeIdentifier=$(Rid) + + + + + + + + AppHost$(ExeExtension) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/GenerateMSBuildExtensions.targets b/src/redist/targets/GenerateMSBuildExtensions.targets new file mode 100644 index 000000000..470c3d473 --- /dev/null +++ b/src/redist/targets/GenerateMSBuildExtensions.targets @@ -0,0 +1,63 @@ + + + + $(BaseOutputPath)$(Configuration)\MSBuildExtensionsLayout\ + + Microsoft.NET.Build.Extensions + $(NuGetPackageRoot)$(MSBuildExtensionsPackageName.ToLower())/$(MicrosoftNETBuildExtensionsPackageVersion.ToLower()) + + NETStandard.Library.NETFramework + $(NuGetPackageRoot)$(NETStandardLibraryNETFrameworkPackageName.ToLower())/$(CLI_NETStandardLibraryNETFrameworkVersion.ToLower()) + + Microsoft.Deployment.DotNet.Releases + $(NuGetPackageRoot)$(DotNetDeploymentReleasesPackageName.ToLower())/$(MicrosoftDeploymentDotNetReleasesVersion.ToLower()) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(MSBuildExtensionsLayoutDirectory)/%(VSMSBuildExtensionsContent.DeploymentSubpath)%(RecursiveDir)%(Filename)%(Extension) + + + + + + + diff --git a/src/redist/targets/GenerateMSIs.targets b/src/redist/targets/GenerateMSIs.targets new file mode 100644 index 000000000..e1dcc4dbc --- /dev/null +++ b/src/redist/targets/GenerateMSIs.targets @@ -0,0 +1,529 @@ + + + + + + https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/wix/Microsoft.Signed.Wix-$(WixVersion).zip + $(ArtifactsDir)Tools/WixTools/$(WixVersion) + $(WixRoot)/WixTools.$(WixVersion).zip + $(WixRoot)/WixDownload.$(WixVersion).sentinel + + + + + $(ArtifactsTmpDir)/$(ArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)-engine.exe + + $(MSBuildThisFileDirectory)../packaging/windows + $(SdkPkgSourcesRootDirectory)/generatemsi.ps1 + $(SdkPkgSourcesRootDirectory)/generatesdkplaceholdermsi.ps1 + $(SdkPkgSourcesRootDirectory)/stablefileidforapphosttransform.xslt + $(SdkPkgSourcesRootDirectory)/generatebundle.ps1 + $(SdkPkgSourcesRootDirectory)/generatenupkg.ps1 + + $(ArtifactsDir)bin/finalizer/$(Architecture)/$(Configuration)/bin/finalizer.exe + + $(SdkPkgSourcesRootDirectory)/VS.Redist.Common.NetCore.Toolset.nuspec + $(ArtifactsNonShippingPackagesDir)VS.Redist.Common.NetCore.Toolset.$(Architecture).$(FullNugetVersion).nupkg + $(SdkPkgSourcesRootDirectory)/VS.Redist.Common.NetCore.SdkPlaceholder.nuspec + $(ArtifactsNonShippingPackagesDir)VS.Redist.Common.NetCore.SdkPlaceholder.$(Architecture).$(FullNugetVersion).nupkg + $(SdkPkgSourcesRootDirectory)/VS.Tools.Net.Core.SDK.nuspec + $(ArtifactsNonShippingPackagesDir)VS.Tools.Net.Core.SDK.$(Architecture).$(FullNugetVersion).nupkg + + + $(ArtifactsDir)bin/SdkResolver/Debug + $(SdkPkgSourcesRootDirectory)/VS.Tools.Net.Core.SDK.Resolver.nuspec + $(ArtifactsNonShippingPackagesDir)VS.Tools.Net.Core.SDK.Resolver.nupkg + + $(SdkPkgSourcesRootDirectory)/VS.Redist.Common.Net.Core.SDK.MSBuildExtensions.nuspec + $(ArtifactsNonShippingPackagesDir)VS.Redist.Common.Net.Core.SDK.MSBuildExtensions.$(FullNugetVersion).nupkg + $(ArtifactsNonShippingPackagesDir)VS.Redist.Common.Net.Core.SDK.MSBuildExtensions.swr + + + $(ArtifactsDir)bin/VSTemplateLocator/Debug + $(SdkPkgSourcesRootDirectory)/VS.Redist.Common.Net.Core.SDK.VSTemplateLocator.nuspec + $(ArtifactsNonShippingPackagesDir)VS.Redist.Common.Net.Core.SDK.VSTemplateLocator.$(FullNugetVersion).nupkg + + + $(ArtifactsObjDir)/LightCommandPackages + + $(ArtifactsNonShippingPackagesDir) + + + + $(ArtifactsNonShippingPackagesDir)$(ArtifactNameWithVersionSdk)$(InstallerExtension) + Dotnet_CLI + $(ArtifactsNonShippingPackagesDir)dotnet-sdkplaceholder-$(FullNugetVersion)-$(ProductMonikerRid)$(InstallerExtension) + NetCore_SdkPlaceholder + $(ArtifactsShippingPackagesDir)$(ArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)$(BundleExtension) + $(ProductBandCombinedHostHostFxrFrameworkSdkName) + + + + + $(_PatchNumber) + + 000000 + + + $(FileVersion) + $(VersionPrefix).$(CombinedBuildNumberAndRevision) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SdkPkgSourcesRootDirectory)/generatetemplatesmsi.ps1 + + + + + $(BaseOutputPath)$(Configuration)\templates-%(TemplatesComponents.TemplatesMajorMinorVersion) + %(TemplatesComponents.BrandNameWithoutVersion) $(Version) + %(TemplatesComponents.MSIVersion) + %(TemplatesComponents.InstallerUpgradeCode) + NetCore_Templates_%(TemplatesComponents.TemplatesMajorMinorVersion) + + + + + + + <_Manifests Include="@(BundledManifests)"> + $([MSBuild]::NormalizePath($([System.IO.Directory]::GetFiles('%(RestoredMsiNupkgContentPath)/data/', '*$(MsiArchitectureForWorkloadManifests).msi')))) + + + + $(IntermediateOutputPath)WorkloadManifests.wxs + + + + + + @(_Manifests->' + + ', ' + ') + + + +]]> + + + + + + + + + %(SDKInternalFiles.Identity) + + + + + + + + $(MinimumVSVersion.Substring(0,$(MinimumVSVersion.LastIndexOf('.')))) + $([MSBuild]::Add($(MinimumVSVersion), .1)) + + + + + + + + + + @(LatestTemplateInstallerComponent->'%(MSIInstallerFile)') + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SdkPkgSourcesRootDirectory)/VS.Redist.Common.NetCore.Templates.nuspec + + + + + $(ArtifactsNonShippingPackagesDir)VS.Redist.Common.NetCore.Templates.%(TemplatesMajorMinorVersion).$(FullNugetVersion).nupkg + %(TemplatesMajorMinorVersion) + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/GeneratePKG.targets b/src/redist/targets/GeneratePKG.targets new file mode 100644 index 000000000..5795c7c34 --- /dev/null +++ b/src/redist/targets/GeneratePKG.targets @@ -0,0 +1,242 @@ + + + + + $(IntermediateOutputPath)pkgs/$(Version) + + + com.microsoft.dotnet.sharedhost.$(SharedHostVersion).component.osx.$(Architecture) + com.microsoft.dotnet.hostfxr.$(HostFxrVersion).component.osx.$(Architecture) + com.microsoft.dotnet.sharedframework.$(SharedFrameworkNugetName).$(MicrosoftNETCoreAppPackageVersion).component.osx.$(Architecture) + com.microsoft.dotnet.pack.targeting.$(MicrosoftNETCoreAppRefPackageVersion).component.osx.$(Architecture) + com.microsoft.dotnet.pack.apphost.$(MicrosoftNETCoreAppHostPackageVersion).component.osx.$(Architecture) + com.microsoft.standard.pack.targeting.$(NETStandardLibraryRefPackageVersion).component.osx.$(Architecture) + com.microsoft.dotnet.dev.$(Version).component.osx.$(Architecture) + com.microsoft.dotnet.dev.$(Version).osx.$(Architecture) + + /usr/local/share/dotnet + /usr/local/share/dotnet/x64 + + $(MSBuildThisFileDirectory)../packaging/osx + $(SdkPkgSourcesRootDirectory)/scripts + $(SdkPkgScriptsDirectory)/postinstall + $(PkgIntermediateDirectory)/scripts + $(SdkPkgDestinationScriptsDirectory)/postinstall + $(PkgIntermediateDirectory)/resources + + $(SdkPkgSourcesRootDirectory)/Distribution-Template + $(SdkPkgSourcesRootDirectory)/Distribution-Template-x64 + $(PkgIntermediateDirectory)/CLI-SDK-Formatted-Distribution-Template.xml + + $(PkgIntermediateDirectory)/$(SdkComponentId).pkg + $(PkgIntermediateDirectory)/$(SharedFrameworkComponentId).pkg + $(PkgIntermediateDirectory)/$(NetCoreAppTargetingPackComponentId).pkg + $(PkgIntermediateDirectory)/$(NetCoreAppHostPackComponentId).pkg + $(PkgIntermediateDirectory)/$(NetStandardTargetingPackComponentId).pkg + $(PkgIntermediateDirectory)/$(SharedHostComponentId).pkg + $(PkgIntermediateDirectory)/$(HostFxrComponentId).pkg + + $(ArtifactsNonShippingPackagesDir)$(ArtifactNameWithVersionSdk)$(InstallerExtension) + $(ArtifactsShippingPackagesDir)$(ArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)$(BundleExtension) + + + + + + + $(SharedFrameworkComponentId) + + + $(NetCoreAppTargetingPackComponentId) + + + $(NetStandardTargetingPackComponentId) + + + $(NetCoreAppHostPackComponentId) + + + $(SharedHostComponentId) + + + $(HostFxrComponentId) + + + $(SdkComponentId) + + + $(Version) + + + $(SdkBrandName) + + + $(SharedFrameworkBrandName) + + + $(NetCoreAppTargetingPackBrandName) + + + $(NetCoreAppHostPackBrandName) + + + $(NetStandardTargetingPackBrandName) + + + $(SharedHostBrandName) + + + $(HostFxrBrandName) + + + $(Architecture) + + + $(Architecture) + x86_64 + + + + 10.15 + 11.0 + + + $(x64EmulationPkgInstallDirectory) + + + + $(Version) + + + + $(Version) + + + $(MicrosoftNETCoreAppRuntimePackageVersion) + + + $(MicrosoftAspNetCoreAppRuntimePackageVersion) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/GenerateRPMs.targets b/src/redist/targets/GenerateRPMs.targets new file mode 100644 index 000000000..6130c8c62 --- /dev/null +++ b/src/redist/targets/GenerateRPMs.targets @@ -0,0 +1,362 @@ + + + + rpm_config.json + $(MSBuildThisFileDirectory)../packaging/rpm/templates + $(MSBuildThisFileDirectory)../packaging/rpm/scripts + after_install_host.sh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(MicrosoftNETCoreAppRuntimeMajorMinorPatchVersion) + + + $(MicrosoftNETCoreAppRefMajorMinorPatchVersion) + + + $(AspNetCoreRuntimeMajorMinorPatchVersion) + + + $(AspNetCoreRefMajorMinorPatchVersion) + + + $(SharedFxRpmPackageName) + + + $(NetCoreAppTargetingPackRpmPackageName) + + + $(NetCoreAppHostPackRpmPackageName) + + + $(AspNetCoreSharedFxRpmPackageName) + + + $(AspNetTargetingPackRpmPackageName) + + + $(Version) + + + amd64 + aarch64 + + + $(SdkBrandName) + + + $(SdkRpmPackageName) + + + + $(Version) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(MajorMinorVersion) + $(ArtifactNameCombinedHostHostFxrFrameworkSdk)-$(SdkRpmPackageVersion) + $(Version) + $(RedistLayoutPath)sdk/ + $(RedistLayoutPath)templates/ + $(ArtifactsShippingPackagesDir)$(DistroSpecificArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)$(InstallerExtension) + $(SdkRPMInstallerFile) + $(ArtifactsShippingPackagesDir)$(MarinerArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)$(InstallerExtension) + $(ArtifactsShippingPackagesDir)$(Mariner2ArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)$(InstallerExtension) + + $(RepoRoot)/Documentation/manpages + $(RepoRoot)/test/EndToEnd/EndToEnd.csproj + $(MSBuildThisFileDirectory)../packaging/rpm/dotnet-config.json + $(IntermediateOutputPath)$(SdkRpmPackageName)/$(RpmPackageVersion) + $(RpmIntermediatesDir)/rpm-testResults.xml + /usr/share/dotnet + + + + $(RpmIntermediatesDir)/RpmLayoutDirectory/ + $(RpmLayoutDirectory)package_root + $(RpmLayoutPackageRoot)/sdk + $(RpmLayoutPackageRoot)/templates + $(RpmLayoutPackageRoot)/sdk-manifests + $(RpmLayoutDirectory)docs + $(RpmLayoutDirectory)templates + $(RpmLayoutDirectory)scripts + + + + $(MicrosoftNETCoreAppMajorMinorVersion) + $(MicrosoftNETCoreAppRuntimePackageVersion) + dotnet-runtime-$(SharedFxRpmPackageVersion) + $(SharedFxRpmPackageName.ToLower()) + dotnet-runtime-$(SharedFxRpmPackageFileVersion) + $(SharedFxRpmPackageFileName.ToLower()) + dotnet-targeting-pack-$(SharedFxRpmPackageVersion) + $(NetCoreAppTargetingPackRpmPackageName.ToLower()) + dotnet-targeting-pack-$(MicrosoftNETCoreAppRefPackageVersion) + $(NetCoreAppTargetingPackRpmPackageFileName.ToLower()) + dotnet-apphost-pack-$(SharedFxRpmPackageVersion) + $(NetCoreAppHostPackRpmPackageName.ToLower()) + dotnet-apphost-pack-$(SharedFxRpmPackageFileVersion) + $(NetCoreAppHostPackRpmPackageFileName.ToLower()) + netstandard-targeting-pack-$(NetStandardTargetingPackMajorMinorVersion) + $(NetStandardTargetingPackRpmPackageName.ToLower()) + netstandard-targeting-pack-$(NETStandardLibraryRefPackageVersion) + $(NetStandardTargetingPackRpmPackageFileName.ToLower()) + dotnet-hostfxr-$(SharedFxRpmPackageVersion) + $(HostFxrRpmPackageName.ToLower()) + dotnet-runtime-deps-$(SharedFxRpmPackageVersion) + $(RuntimeDepsPackageName.ToLower()) + dotnet-host + aspnetcore-runtime-$(AspNetCoreMajorMinorVersion) + $(AspNetCoreSharedFxRpmPackageName.ToLower()) + aspnetcore-runtime-$(MicrosoftAspNetCoreRuntimePackageVersion) + $(AspNetCoreSharedFxRpmPackageFileName.ToLower()) + aspnetcore-targeting-pack-$(AspNetCoreMajorMinorVersion) + $(AspNetTargetingPackRpmPackageName.ToLower()) + aspnetcore-targeting-pack-$(MicrosoftAspNetCoreRefPackageVersion) + $(AspNetTargetingPackRpmPackageFileName.ToLower()) + $(ScriptsDir)/$(AfterInstallHostScriptName) + $(RpmLayoutScripts)/$(AfterInstallHostScriptName) + + + + $(DownloadsFolder)$(DownloadedNetCoreAppTargetingPackInstallerFileName) + $(DownloadsFolder)$(DownloadedNetStandardTargetingPackInstallerFileName) + $(DownloadsFolder)$(DownloadedNetCoreAppHostPackInstallerFileName) + $(DownloadsFolder)$(DownloadedAspNetTargetingPackInstallerFileName) + $(DownloadsFolder)$(DownloadedRuntimeDepsInstallerFileName) + $(DownloadsFolder)$(DownloadedSharedHostInstallerFileName) + $(DownloadsFolder)$(DownloadedHostFxrInstallerFileName) + $(DownloadsFolder)$(DownloadedSharedFrameworkInstallerFileName) + $(DownloadsFolder)$(DownloadedAspNetCoreSharedFxInstallerFileName) + + + + + + + + + + + + + false + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/GetRuntimeInformation.targets b/src/redist/targets/GetRuntimeInformation.targets new file mode 100644 index 000000000..e05828c83 --- /dev/null +++ b/src/redist/targets/GetRuntimeInformation.targets @@ -0,0 +1,59 @@ + + + + + + $(__DistroRid) + $([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier) + win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant) + + True + win + osx + freebsd + linux + + $(Rid.Substring(0, $(Rid.LastIndexOf('-')))) + $(HostOSName) + + $(OSName) + + $(OSName)-$(Architecture) + + $(PortableOSName)-$(Architecture) + + + + true + true + true + true + true + + + + $(Rid) + + $(PortableRid) + + dotnet-sdk-internal$(PgoTerm) + dotnet-sdk$(PgoTerm) + + $(ArtifactNameSdk)-$(Version)-$(ProductMonikerRid) + dotnet-standard-support-vs2015-$(Version)-$(ProductMonikerRid) + $(ArtifactNameCombinedHostHostFxrFrameworkSdk)-$(Version)-$(ProductMonikerRid) + + Dotnet SDK Bundle Installer $(CliProductBandVersion) $(ProductMonikerRid) + + $(Architecture) + aarch64 + + $(ArtifactNameCombinedHostHostFxrFrameworkSdk)-$(Version)-$(InstallerTargetArchitecture) + + $(ArtifactNameCombinedHostHostFxrFrameworkSdk)-$(Version)-cm.1-$(InstallerTargetArchitecture) + $(ArtifactNameCombinedHostHostFxrFrameworkSdk)-$(Version)-cm.2-$(InstallerTargetArchitecture) + + + + diff --git a/src/redist/targets/LinuxNativeInstallerDependencyVersions.targets b/src/redist/targets/LinuxNativeInstallerDependencyVersions.targets new file mode 100644 index 000000000..79941d045 --- /dev/null +++ b/src/redist/targets/LinuxNativeInstallerDependencyVersions.targets @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/redist/targets/SetBuildDefaults.targets b/src/redist/targets/SetBuildDefaults.targets new file mode 100644 index 000000000..5e6a4bd72 --- /dev/null +++ b/src/redist/targets/SetBuildDefaults.targets @@ -0,0 +1,37 @@ + + + + + + + true + false + + true + true + + + + + + + + + + + + + : + %3b + + + + diff --git a/src/redist/targets/Versions.targets b/src/redist/targets/Versions.targets new file mode 100644 index 000000000..d71565b42 --- /dev/null +++ b/src/redist/targets/Versions.targets @@ -0,0 +1,11 @@ + + + + + $(VersionPrefix)-$(PreReleaseVersionLabel) + $(FullNugetVersion).$(PreReleaseVersionIteration) + $(FullNugetVersion).$(VersionSuffixDateStamp).$(VersionSuffixBuildOfTheDay) + + + + diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 000000000..a953b97cc --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,4 @@ + + + + diff --git a/test/EndToEnd/EndToEnd.Tests.csproj b/test/EndToEnd/EndToEnd.Tests.csproj new file mode 100644 index 000000000..22cf49211 --- /dev/null +++ b/test/EndToEnd/EndToEnd.Tests.csproj @@ -0,0 +1,28 @@ + + + + $(CoreSdkTargetFramework) + $(DefineConstants);LINUX_PORTABLE + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/test/EndToEnd/GivenDotNetLinuxInstallers.cs b/test/EndToEnd/GivenDotNetLinuxInstallers.cs new file mode 100644 index 000000000..1eba2dc51 --- /dev/null +++ b/test/EndToEnd/GivenDotNetLinuxInstallers.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; + +namespace Microsoft.DotNet.Tests.EndToEnd +{ + public class GivenDotNetLinuxInstallers + { + [Fact] + public void ItHasExpectedDependencies() + { + var installerFile = Environment.GetEnvironmentVariable("SDK_INSTALLER_FILE"); + if (string.IsNullOrEmpty(installerFile)) + { + return; + } + + var ext = Path.GetExtension(installerFile); + switch (ext) + { + case ".deb": + DebianPackageHasDependencyOnAspNetCoreStoreAndDotnetRuntime(installerFile); + return; + case ".rpm": + RpmPackageHasDependencyOnAspNetCoreStoreAndDotnetRuntime(installerFile); + return; + } + } + + private void DebianPackageHasDependencyOnAspNetCoreStoreAndDotnetRuntime(string installerFile) => + // Example output: + + // $ dpkg --info dotnet-sdk-2.1.105-ubuntu-x64.deb + + // new debian package, version 2.0. + // size 75660448 bytes: control archive=29107 bytes. + // 717 bytes, 11 lines control + // 123707 bytes, 1004 lines md5sums + // 1710 bytes, 28 lines * postinst #!/usr/bin/env + // Package: dotnet-sdk-2.1.104 + // Version: 2.1.104-1 + // Architecture: amd64 + // Maintainer: Microsoft + // Installed-Size: 201119 + // Depends: dotnet-runtime-2.0.6, aspnetcore-store-2.0.6 + // Section: devel + // Priority: standard + // Homepage: https://dotnet.github.io/core + // Description: Microsoft .NET Core SDK - 2.1.104 + + new TestCommand("dpkg") + .ExecuteWithCapturedOutput($"--info {installerFile}") + .Should().Pass() + .And.HaveStdOutMatching(@"Depends:.*\s?dotnet-runtime-\d+(\.\d+){2}") + .And.HaveStdOutMatching(@"Depends:.*\s?aspnetcore-store-\d+(\.\d+){2}"); + + private void RpmPackageHasDependencyOnAspNetCoreStoreAndDotnetRuntime(string installerFile) => + // Example output: + + // $ rpm -qpR dotnet-sdk-2.1.105-rhel-x64.rpm + + // dotnet-runtime-2.0.7 >= 2.0.7 + // aspnetcore-store-2.0.7 >= 2.0.7 + // /bin/sh + // /bin/sh + // rpmlib(PayloadFilesHavePrefix) <= 4.0-1 + // rpmlib(CompressedFileNames) <= 3.0.4-1 + + new TestCommand("rpm") + .ExecuteWithCapturedOutput($"-qpR {installerFile}") + .Should().Pass() + .And.HaveStdOutMatching(@"dotnet-runtime-\d+(\.\d+){2} >= \d+(\.\d+){2}") + .And.HaveStdOutMatching(@"aspnetcore-store-\d+(\.\d+){2} >= \d+(\.\d+){2}"); + } +} diff --git a/test/EndToEnd/GivenDotnetUsesDotnetTools.cs b/test/EndToEnd/GivenDotnetUsesDotnetTools.cs new file mode 100644 index 000000000..cfdf5d590 --- /dev/null +++ b/test/EndToEnd/GivenDotnetUsesDotnetTools.cs @@ -0,0 +1,13 @@ +using FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; + +namespace EndToEnd +{ + public class GivenDotnetUsesDotnetTools : TestBase + { + [RequiresAspNetCore] + public void ThenOneDotnetToolsCanBeCalled() => new DotnetCommand() + .ExecuteWithCapturedOutput("dev-certs --help") + .Should().Pass(); + } +} diff --git a/test/EndToEnd/GivenFrameworkDependentApps.cs b/test/EndToEnd/GivenFrameworkDependentApps.cs new file mode 100644 index 000000000..1e3d0d806 --- /dev/null +++ b/test/EndToEnd/GivenFrameworkDependentApps.cs @@ -0,0 +1,119 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Linq; +using FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; +using NuGet.ProjectModel; +using NuGet.Versioning; +using Xunit; + +namespace EndToEnd +{ + public class GivenFrameworkDependentApps : TestBase + { + [Theory] + [ClassData(typeof(SupportedNetCoreAppVersions))] + public void ItDoesNotRollForwardToTheLatestVersionOfNetCore(string minorVersion) + { + if (minorVersion == "3.0" || minorVersion == "3.1" || minorVersion == "5.0" || minorVersion == "6.0" || minorVersion == "7.0" || minorVersion == "8.0" || minorVersion == "9.0") + { + // https://github.com/dotnet/core-sdk/issues/621 + return; + } + ItDoesNotRollForwardToTheLatestVersion(TestProjectCreator.NETCorePackageName, minorVersion); + } + + [Theory] + [ClassData(typeof(SupportedAspNetCoreVersions))] + public void ItDoesNotRollForwardToTheLatestVersionOfAspNetCoreApp(string minorVersion) + { + if (minorVersion == "3.0" || minorVersion == "3.1" || minorVersion == "5.0" || minorVersion == "6.0" || minorVersion == "7.0" || minorVersion == "8.0" || minorVersion == "9.0") + { + // https://github.com/dotnet/core-sdk/issues/621 + return; + } + ItDoesNotRollForwardToTheLatestVersion(TestProjectCreator.AspNetCoreAppPackageName, minorVersion); + } + + [Theory] + [ClassData(typeof(SupportedAspNetCoreAllVersions))] + public void ItDoesNotRollForwardToTheLatestVersionOfAspNetCoreAll(string minorVersion) => ItDoesNotRollForwardToTheLatestVersion(TestProjectCreator.AspNetCoreAllPackageName, minorVersion); + + internal void ItDoesNotRollForwardToTheLatestVersion(string packageName, string minorVersion) + { + // https://github.com/NuGet/Home/issues/8571 + #if LINUX_PORTABLE + return; + #else + var testProjectCreator = new TestProjectCreator() + { + PackageName = packageName, + MinorVersion = minorVersion, + }; + + var _testInstance = testProjectCreator.Create(); + + string projectDirectory = _testInstance.Root.FullName; + + string projectPath = Path.Combine(projectDirectory, "TestAppSimple.csproj"); + + // Get the resolved version of .NET Core + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + string assetsFilePath = Path.Combine(projectDirectory, "obj", "project.assets.json"); + var assetsFile = new LockFileFormat().Read(assetsFilePath); + + var versionInAssertsJson = GetPackageVersion(assetsFile, packageName); + versionInAssertsJson.Should().NotBeNull(); + + if (versionInAssertsJson.IsPrerelease && versionInAssertsJson.Patch == 0) + { + // if the bundled version is, for example, a prerelease of + // .NET Core 2.1.1, that we don't roll forward to that prerelease + // version for framework-dependent deployments. + return; + } + + versionInAssertsJson.ToNormalizedString().Should().BeEquivalentTo(GetExpectedVersion(packageName, minorVersion)); + #endif + } + + private static NuGetVersion GetPackageVersion(LockFile lockFile, string packageName) => lockFile?.Targets?.SingleOrDefault(t => t.RuntimeIdentifier == null) + ?.Libraries?.SingleOrDefault(l => + string.Compare(l.Name, packageName, StringComparison.CurrentCultureIgnoreCase) == 0) + ?.Version; + + public string GetExpectedVersion(string packageName, string minorVersion) + { + if (minorVersion.StartsWith("1.0")) + { + return "1.0.5"; // special case for 1.0 + } + else if (minorVersion.StartsWith("1.1")) + { + return "1.1.2"; // special case for 1.1 + } + else + { + // ASP.NET 2.1.0 packages had exact version dependencies, which was problematic, + // so the default version for 2.1 apps is 2.1.1. + if (packageName == TestProjectCreator.AspNetCoreAppPackageName || + packageName == TestProjectCreator.AspNetCoreAllPackageName) + { + if (minorVersion == "2.1") + { + return "2.1.1"; + } + } + var parsed = NuGetVersion.Parse(minorVersion); + return new NuGetVersion(parsed.Major, parsed.Minor, 0).ToNormalizedString(); + } + } + } +} diff --git a/test/EndToEnd/GivenNetFrameworkSupportsNetStandard2.cs b/test/EndToEnd/GivenNetFrameworkSupportsNetStandard2.cs new file mode 100644 index 000000000..7401426ad --- /dev/null +++ b/test/EndToEnd/GivenNetFrameworkSupportsNetStandard2.cs @@ -0,0 +1,37 @@ +using FluentAssertions; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using System.IO; + +namespace EndToEnd +{ + public class GivenNetFrameworkSupportsNetStandard2 : TestBase + { + [WindowsOnlyFact] + public void ANET461ProjectCanReferenceANETStandardProject() + { + var _testInstance = TestAssets.Get(TestAssetKinds.DesktopTestProjects, "NETFrameworkReferenceNETStandard20") + .CreateInstance() + .WithSourceFiles(); + + string projectDirectory = Path.Combine(_testInstance.Root.FullName, "TestApp"); + + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + new BuildCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + new RunCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput() + .Should().Pass() + .And.HaveStdOutContaining("This string came from the test library!"); + + } + } +} diff --git a/test/EndToEnd/GivenSelfContainedAppsRollForward.cs b/test/EndToEnd/GivenSelfContainedAppsRollForward.cs new file mode 100644 index 000000000..6c8160627 --- /dev/null +++ b/test/EndToEnd/GivenSelfContainedAppsRollForward.cs @@ -0,0 +1,153 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Xml.Linq; +using FluentAssertions; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using NuGet.ProjectModel; +using NuGet.Versioning; +using Xunit; + +namespace EndToEnd +{ + public partial class GivenSelfContainedAppsRollForward : TestBase + { + internal void ItRollsForwardToTheLatestVersion(string packageName, string minorVersion) + { + var testProjectCreator = new TestProjectCreator() + { + PackageName = packageName, + MinorVersion = minorVersion, + // Set RuntimeIdentifier to opt in to roll-forward behavior + RuntimeIdentifier = RuntimeInformation.RuntimeIdentifier + }; + + var testInstance = testProjectCreator.Create(); + + string projectDirectory = testInstance.Root.FullName; + + // Get the version rolled forward to + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + string assetsFilePath = Path.Combine(projectDirectory, "obj", "project.assets.json"); + var assetsFile = new LockFileFormat().Read(assetsFilePath); + + var rolledForwardVersion = GetPackageVersion(assetsFile, packageName); + rolledForwardVersion.Should().NotBeNull(); + + if (rolledForwardVersion.IsPrerelease) + { + // If this version of .NET Core is still prerelease, then: + // - Floating the patch by adding ".*" to the major.minor version won't work, but + // - There aren't any patches to roll-forward to, so we skip testing this until the version + // leaves prerelease. + return; + } + + testProjectCreator.Identifier = "floating"; + + var floatingProjectInstance = testProjectCreator.Create(); + + var floatingProjectPath = Path.Combine(floatingProjectInstance.Root.FullName, "TestAppSimple.csproj"); + + var floatingProject = XDocument.Load(floatingProjectPath); + var ns = floatingProject.Root.Name.Namespace; + + + if (packageName == TestProjectCreator.NETCorePackageName) + { + // Float the RuntimeFrameworkVersion to get the latest version of the runtime available from feeds + floatingProject.Root.Element(ns + "PropertyGroup") + .Add(new XElement(ns + "RuntimeFrameworkVersion", $"{minorVersion}.*")); + } + else + { + floatingProject.Root.Element(ns + "ItemGroup") + .Element(ns + "PackageReference") + .Add(new XAttribute("Version", $"{minorVersion}.*"), + new XAttribute("AllowExplicitVersion", "true")); + } + + floatingProject.Save(floatingProjectPath); + + new RestoreCommand() + .WithWorkingDirectory(floatingProjectInstance.Root.FullName) + .Execute() + .Should().Pass(); + + string floatingAssetsFilePath = Path.Combine(floatingProjectInstance.Root.FullName, "obj", "project.assets.json"); + + var floatedAssetsFile = new LockFileFormat().Read(floatingAssetsFilePath); + + var floatedVersion = GetPackageVersion(floatedAssetsFile, packageName); + floatedVersion.Should().NotBeNull(); + + rolledForwardVersion.ToNormalizedString().Should().BeEquivalentTo(floatedVersion.ToNormalizedString(), + $"the latest patch version for {packageName} {minorVersion} in Microsoft.NETCoreSdk.BundledVersions.props " + + "needs to be updated (see the ImplicitPackageVariable items in MSBuildExtensions.targets in this repo)"); + } + + private static NuGetVersion GetPackageVersion(LockFile lockFile, string packageName) => lockFile?.Targets?.SingleOrDefault(t => t.RuntimeIdentifier != null) + ?.Libraries?.SingleOrDefault(l => + string.Compare(l.Name, packageName, StringComparison.CurrentCultureIgnoreCase) == 0) + ?.Version; + + [Fact] + public void WeCoverLatestNetCoreAppRollForward() + { + // Run "dotnet new console", get TargetFramework property, and make sure it's covered in SupportedNetCoreAppVersions + var directory = TestAssets.CreateTestDirectory(); + string projectDirectory = directory.FullName; + + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute("console --no-restore") + .Should().Pass(); + + string projectPath = Path.Combine(projectDirectory, Path.GetFileName(projectDirectory) + ".csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + string targetFramework = project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework") + .Value; + + SupportedNetCoreAppVersions.TargetFrameworkShortFolderVersion + .Should().Contain(targetFramework, $"the {nameof(SupportedNetCoreAppVersions)}.{nameof(SupportedNetCoreAppVersions.Versions)} property should include the default version " + + "of .NET Core created by \"dotnet new\""); + } + + [Fact] + public void WeCoverLatestAspNetCoreAppRollForward() + { + var directory = TestAssets.CreateTestDirectory(); + string projectDirectory = directory.FullName; + + // Run "dotnet new web", get TargetFramework property, and make sure it's covered in SupportedAspNetCoreAppVersions + + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute("web --no-restore") + .Should().Pass(); + + string projectPath = Path.Combine(projectDirectory, Path.GetFileName(projectDirectory) + ".csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + string targetFramework = project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework") + .Value; + + TargetFrameworkHelper.GetNetAppTargetFrameworks(SupportedAspNetCoreVersions.Versions) + .Should().Contain(targetFramework, $"the {nameof(SupportedAspNetCoreVersions)} should include the default version " + + "of Microsoft.AspNetCore.App used by the templates created by \"dotnet new web\""); + } + } +} diff --git a/test/EndToEnd/GivenUnixPlatform.cs b/test/EndToEnd/GivenUnixPlatform.cs new file mode 100644 index 000000000..690e1530e --- /dev/null +++ b/test/EndToEnd/GivenUnixPlatform.cs @@ -0,0 +1,25 @@ +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; + +namespace EndToEnd.Tests +{ + public class GivenUnixPlatform : TestBase + { + [UnixOnlyTheory(Skip="https://github.com/dotnet/templating/issues/1979")] + [InlineData("wpf")] + [InlineData("winforms")] + public void ItDoesNotIncludeWindowsOnlyProjectTemplates(string template) + { + var directory = TestAssets.CreateTestDirectory(); + + new NewCommandShim() + .WithWorkingDirectory(directory.FullName) + .Execute(template) + .Should() + .Fail() + .And + .HaveStdErrContaining($": {template}."); + } + } +} diff --git a/test/EndToEnd/GivenUsingDefaultRuntimeFrameworkVersions.cs b/test/EndToEnd/GivenUsingDefaultRuntimeFrameworkVersions.cs new file mode 100644 index 000000000..de7655b86 --- /dev/null +++ b/test/EndToEnd/GivenUsingDefaultRuntimeFrameworkVersions.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; +using NuGet.Versioning; +using Xunit; + +namespace EndToEnd +{ + public partial class GivenUsingDefaultRuntimeFrameworkVersions : TestBase + { + private static readonly IEnumerable frameworks = new string[] {"Microsoft.NETCore.App", "Microsoft.WindowsDesktop.App", + "Microsoft.WindowsDesktop.App.WPF", "Microsoft.WindowsDesktop.App.WindowsForms", "Microsoft.AspNetCore.App" }; + + private static readonly IEnumerable versions = SupportedNetCoreAppVersions.Versions.Where(version => NuGetVersion.Parse(version).Major >= 3); + + [Fact] + public void DefaultRuntimeVersionsAreUpToDate() + { + var outputFile = "resolvedVersions.txt"; + var testProjectCreator = new TestProjectCreator() + { + PackageName = "DefaultRuntimeVersionsAreUpToDate", + MinorVersion = "3.0" + }; + var testProject = testProjectCreator.Create(); + + var projectFile = new DirectoryInfo(testProject.Root.FullName).GetFiles("*.csproj").First().FullName; + var project = XDocument.Load(projectFile); + string writeResolvedVersionsTarget = @$" + + + + + + + "; + writeResolvedVersionsTarget = writeResolvedVersionsTarget.Replace('`', '"'); + var targetElement = XElement.Parse(writeResolvedVersionsTarget); + var ns = project.Root.Name.Namespace; + foreach (var elem in targetElement.Descendants()) + elem.Name = ns + elem.Name.LocalName; + targetElement.Name = ns + targetElement.Name.LocalName; + project.Root.Add(targetElement); + using (var file = File.CreateText(projectFile)) + { + project.Save(file); + } + + new RestoreCommand() + .WithWorkingDirectory(testProject.Root.FullName) + .Execute() + .Should().Pass(); + + var binDirectory = new DirectoryInfo(testProject.Root.FullName).Sub("bin").Sub("Debug").GetDirectories().FirstOrDefault(); + binDirectory.Should().HaveFilesMatching(outputFile, SearchOption.TopDirectoryOnly); + var resolvedVersionsFile = File.ReadAllLines(Path.Combine(binDirectory.FullName, outputFile)); + foreach (var framework in frameworks) + { + foreach (var version in versions) + { + var frameworkVersionLine = resolvedVersionsFile.Where(line => line.Contains(framework) && line.Contains(version)).FirstOrDefault(); + if (!string.IsNullOrEmpty(frameworkVersionLine)) + { + var defaultVersion = NuGetVersion.Parse(frameworkVersionLine.Split(" ")[1]); + var latestVersion = NuGetVersion.Parse(frameworkVersionLine.Split(" ")[2]); + + if (latestVersion.Patch == 0 && latestVersion.IsPrerelease) + { + defaultVersion.Should().Be(latestVersion, + $"the DefaultRuntimeFrameworkVersion for { framework } { version } in Microsoft.NETCoreSdk.BundledVersions.props does not match latest prerelease version { latestVersion }"); + } + else + { + defaultVersion.Should().Be(new NuGetVersion(latestVersion.Major, latestVersion.Minor, 0), + $"the DefaultRuntimeFrameworkVersion for { framework } { version } in Microsoft.NETCoreSdk.BundledVersions.props needs to be updated to { version }.0"); + } + } + } + } + } + } +} diff --git a/test/EndToEnd/GivenWeWantToRequireWindowsForDesktopApps.cs b/test/EndToEnd/GivenWeWantToRequireWindowsForDesktopApps.cs new file mode 100644 index 000000000..be6110b23 --- /dev/null +++ b/test/EndToEnd/GivenWeWantToRequireWindowsForDesktopApps.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using System.Xml.Linq; +using EndToEnd; +using FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; + +namespace Microsoft.DotNet.Tests.EndToEnd +{ + public class GivenWeWantToRequireWindowsForDesktopApps + { + [Fact] + public void It_does_not_download_desktop_targeting_packs_on_unix() + { + var testProjectCreator = new TestProjectCreator() + { + MinorVersion = "9.0" + }; + + testProjectCreator.AdditionalProperties["RestorePackagesPath"] = @"$(MSBuildProjectDirectory)\packages"; + testProjectCreator.AdditionalProperties["OutputType"] = "exe"; + + var testInstance = testProjectCreator.Create(); + + new BuildCommand() + .WithWorkingDirectory(testInstance.Root.FullName) + .Execute() + .Should().Pass(); + + string packagesPath = Path.Combine(testInstance.Root.FullName, "packages"); + Directory.Exists(packagesPath).Should().BeFalse(packagesPath + " should not exist"); + } + + [PlatformSpecificFact(TestPlatforms.Linux | TestPlatforms.OSX | TestPlatforms.FreeBSD)] + public void It_does_not_download_desktop_runtime_packs_on_unix() + { + const string Rid = "win-x64"; + + var testProjectCreator = new TestProjectCreator() + { + MinorVersion = "3.1" + }; + + testProjectCreator.AdditionalProperties["RestorePackagesPath"] = @"$(MSBuildProjectDirectory)\packages"; + testProjectCreator.AdditionalProperties["OutputType"] = "exe"; + testProjectCreator.AdditionalProperties["RuntimeIdentifier"] = Rid; + + // At certain point of the release cycle LatestRuntimeFrameworkVersion in eng folder may not exist on the nuget feed + static void overrideLastRuntimeFrameworkVersionToExistingOlderVersion(XDocument project) + { + XNamespace ns = project.Root.Name.Namespace; + var target = XElement.Parse(@" + + 3.1.10 + + + + 3.1.10 + + "); + target.Name = ns + target.Name.LocalName; + project.Root.Add(target); + } + TestFramework.TestAssetInstance testInstance + = testProjectCreator.Create().WithProjectChanges(overrideLastRuntimeFrameworkVersionToExistingOlderVersion); + + new PublishCommand() + .WithWorkingDirectory(testInstance.Root.FullName) + .Execute() + .Should().Pass(); + + string packagesPath = Path.Combine(testInstance.Root.FullName, "packages", $"runtime.{Rid}.microsoft.windowsdesktop.app"); + Directory.Exists(packagesPath).Should().BeFalse(packagesPath + " should not exist"); + } + } +} diff --git a/test/EndToEnd/GivenWindowsApp.cs b/test/EndToEnd/GivenWindowsApp.cs new file mode 100644 index 000000000..f0a336441 --- /dev/null +++ b/test/EndToEnd/GivenWindowsApp.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using System.Xml.Linq; +using FluentAssertions; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; + +namespace EndToEnd +{ + public class GivenWindowsApp : TestBase + { + [WindowsOnlyTheory] + [InlineData("10.0.17763.0")] + [InlineData("10.0.18362.0")] + [InlineData("10.0.19041.0")] + [InlineData("10.0.20348.0")] + [InlineData("10.0.22000.0")] + [InlineData("10.0.22621.0")] + public void ItCanBuildAndRun(string targetPlatformVersion) + { + var testInstance = TestAssets.Get(TestAssetKinds.TestProjects, "UseCswinrt") + .CreateInstance("UseCswinrt" + targetPlatformVersion) + .WithSourceFiles(); + + var projectPath = Path.Combine(testInstance.Root.FullName, "consolecswinrt.csproj"); + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + // Update TargetFramework to the right version of .NET Core + project.Root.Element(ns + "PropertyGroup") + .Add(new XElement(ns + "TargetPlatformVersion", targetPlatformVersion)); + project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework").Value = TestAssetInfo.currentTfm; + + project.Save(projectPath); + + new BuildCommand() + .WithProjectFile(new FileInfo(testInstance.Root.FullName)) + .Execute() + .Should().Pass(); + + new RunCommand().WithWorkingDirectory(testInstance.Root.FullName) + .Execute("--no-build") + .Should().Pass().And.HaveStdOutContaining("Hello"); + } + } +} diff --git a/test/EndToEnd/ProjectBuildTests.cs b/test/EndToEnd/ProjectBuildTests.cs new file mode 100644 index 000000000..da0893539 --- /dev/null +++ b/test/EndToEnd/ProjectBuildTests.cs @@ -0,0 +1,506 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Xml.Linq; +using FluentAssertions; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; + +namespace EndToEnd.Tests +{ + public class ProjectBuildTests : TestBase + { + [Fact] + public void ItCanNewRestoreBuildRunCleanMSBuildProject() + { + var directory = TestAssets.CreateTestDirectory(); + string projectDirectory = directory.FullName; + + string newArgs = "console --no-restore"; + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute(newArgs) + .Should().Pass(); + + string projectPath = Path.Combine(projectDirectory, directory.Name + ".csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework").Value = TestAssetInfo.currentTfm; + project.Save(projectPath); + + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + new BuildCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + var runCommand = new RunCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput() + .Should().Pass().And.HaveStdOutContaining("Hello, World!"); + + var binDirectory = new DirectoryInfo(projectDirectory).Sub("bin"); + binDirectory.Should().HaveFilesMatching("*.dll", SearchOption.AllDirectories); + + new CleanCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + binDirectory.Should().NotHaveFilesMatching("*.dll", SearchOption.AllDirectories); + } + + [Fact] + public void ItCanRunAnAppUsingTheWebSdk() + { + var directory = TestAssets.CreateTestDirectory(); + string projectDirectory = directory.FullName; + + string newArgs = "console --no-restore"; + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute(newArgs) + .Should().Pass(); + + string projectPath = Path.Combine(projectDirectory, directory.Name + ".csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + project.Root.Attribute("Sdk").Value = "Microsoft.NET.Sdk.Web"; + project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework").Value = TestAssetInfo.currentTfm; + project.Save(projectPath); + + new BuildCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + var runCommand = new RunCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput() + .Should().Pass().And.HaveStdOutContaining("Hello, World!"); + } + + [WindowsOnlyTheory] + // [InlineData("net6.0", true)] + // [InlineData("net6.0", false)] + [InlineData("current", true)] + [InlineData("current", false)] + public void ItCanPublishArm64Winforms(string TargetFramework, bool selfContained) + { + DirectoryInfo directory = TestAssets.CreateTestDirectory(); + string projectDirectory = directory.FullName; + string TargetFrameworkParameter = ""; + + if (TargetFramework != "current") + { + TargetFrameworkParameter = $"-f {TargetFramework}"; + } + string newArgs = $"winforms {TargetFrameworkParameter} --no-restore"; + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute(newArgs) + .Should().Pass(); + + string selfContainedArgs = selfContained ? " --self-contained" : ""; + string publishArgs = "-r win-arm64" + selfContainedArgs; + new PublishCommand() + .WithWorkingDirectory(projectDirectory) + .Execute(publishArgs) + .Should().Pass(); + + var selfContainedPublishDir = new DirectoryInfo(projectDirectory) + .Sub("bin").Sub(TargetFramework != "current" ? "Debug" : "Release").GetDirectories().FirstOrDefault() + .Sub("win-arm64").Sub("publish"); + + if (selfContained) + { + selfContainedPublishDir.Should().HaveFilesMatching("System.Windows.Forms.dll", SearchOption.TopDirectoryOnly); + } + selfContainedPublishDir.Should().HaveFilesMatching($"{directory.Name}.dll", SearchOption.TopDirectoryOnly); + } + + [WindowsOnlyTheory] + // [InlineData("net6.0", true)] + // [InlineData("net6.0", false)] + [InlineData("current", true)] + [InlineData("current", false)] + public void ItCanPublishArm64Wpf(string TargetFramework, bool selfContained) + { + DirectoryInfo directory = TestAssets.CreateTestDirectory(); + string projectDirectory = directory.FullName; + string TargetFrameworkParameter = ""; + + if (TargetFramework != "current") + { + TargetFrameworkParameter = $"-f {TargetFramework}"; + } + + string newArgs = $"wpf {TargetFrameworkParameter} --no-restore"; + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute(newArgs) + .Should().Pass(); + + string selfContainedArgs = selfContained ? " --self-contained" : ""; + string publishArgs = "-r win-arm64" + selfContainedArgs; + new PublishCommand() + .WithWorkingDirectory(projectDirectory) + .Execute(publishArgs) + .Should().Pass(); + + var selfContainedPublishDir = new DirectoryInfo(projectDirectory) + .Sub("bin").Sub(TargetFramework != "current" ? "Debug" : "Release").GetDirectories().FirstOrDefault() + .Sub("win-arm64").Sub("publish"); + + if (selfContained) + { + selfContainedPublishDir.Should().HaveFilesMatching("PresentationCore.dll", SearchOption.TopDirectoryOnly); + selfContainedPublishDir.Should().HaveFilesMatching("PresentationNative_*.dll", SearchOption.TopDirectoryOnly); + } + selfContainedPublishDir.Should().HaveFilesMatching($"{directory.Name}.dll", SearchOption.TopDirectoryOnly); + } + + [Theory] + // microsoft.dotnet.common.projectemplates templates + [InlineData("console")] + [InlineData("console", "C#")] + [InlineData("console", "VB")] + [InlineData("console", "F#")] + [InlineData("classlib")] + [InlineData("classlib", "C#")] + [InlineData("classlib", "VB")] + [InlineData("classlib", "F#")] + + [InlineData("mstest")] + [InlineData("nunit")] + [InlineData("web")] + [InlineData("mvc")] + public void ItCanBuildTemplates(string templateName, string language = "") => TestTemplateCreateAndBuild(templateName, language: language); + + /// + /// The test checks if dotnet new shows curated list correctly after the SDK installation and template insertion. + /// + [Fact] + public void DotnetNewShowsCuratedListCorrectly() + { + string locale = System.Threading.Thread.CurrentThread.CurrentUICulture.Name; + if (!string.IsNullOrWhiteSpace(locale) + && !locale.StartsWith("en", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"[{nameof(DotnetNewShowsCuratedListCorrectly)}] CurrentUICulture: {locale}"); + Console.WriteLine($"[{nameof(DotnetNewShowsCuratedListCorrectly)}] Test is skipped as it supports only 'en' or invariant culture."); + return; + } + + string expectedOutput = +@"[\-\s]+ +[\w \.\(\)]+blazor\s+\[C#\][\w\ \/]+ +[\w \.\(\)]+classlib\s+\[C#\],F#,VB[\w\ \/]+ +[\w \.\(\)]+console\s+\[C#\],F#,VB[\w\ \/]+ +"; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + expectedOutput += +@"[\w \.\(\)]+winforms\s+\[C#\],VB[\w\ \/]+ +[\w \.\(\)]+\wpf\s+\[C#\],VB[\w\ \/]+ +"; + } + //list should end with new line + expectedOutput += Environment.NewLine; + + new NewCommandShim() + .Execute() + .Should().Pass() + .And.HaveStdOutMatching(expectedOutput); + } + + [Theory] + // microsoft.dotnet.common.itemtemplates templates + [InlineData("globaljson")] + [InlineData("nugetconfig")] + [InlineData("webconfig")] + [InlineData("gitignore")] + [InlineData("tool-manifest")] + [InlineData("sln")] + public void ItCanCreateItemTemplate(string templateName) + { + DirectoryInfo directory = TestAssets.CreateTestDirectory(identifier: templateName); + string projectDirectory = directory.FullName; + + string newArgs = $"{templateName}"; + + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute(newArgs) + .Should().Pass(); + + //check if the template created files + Assert.True(directory.Exists); + Assert.True(directory.EnumerateFileSystemInfos().Any()); + + // delete test directory for some tests so we aren't leaving behind non-compliant nuget files + if (templateName.Equals("nugetconfig")) + { + directory.Delete(true); + } + } + + [Theory] + // microsoft.dotnet.common.itemtemplates templates + [InlineData("class")] + [InlineData("struct")] + [InlineData("enum")] + [InlineData("record")] + [InlineData("interface")] + [InlineData("class", "C#")] + [InlineData("class", "VB")] + [InlineData("struct", "VB")] + [InlineData("enum", "VB")] + [InlineData("interface", "VB")] + public void ItCanCreateItemTemplateWithProjectRestriction(string templateName, string language = "") + { + var languageExtensionMap = new Dictionary() + { + { "", ".cs" }, + { "C#", ".cs" }, + { "VB", ".vb" } + }; + + DirectoryInfo directory = InstantiateProjectTemplate("classlib", language, withNoRestore: false); + string projectDirectory = directory.FullName; + string expectedItemName = $"TestItem_{templateName}"; + string newArgs = $"{templateName} --name {expectedItemName} --debug:ephemeral-hive"; + if (!string.IsNullOrWhiteSpace(language)) + { + newArgs += $" --language {language}"; + } + + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute(newArgs) + .Should().Pass(); + + //check if the template created files + Assert.True(directory.Exists); + Assert.True(directory.EnumerateFileSystemInfos().Any()); + Assert.True(directory.GetFile($"{expectedItemName}.{languageExtensionMap[language]}") != null); + } + + [WindowsOnlyTheory] + [InlineData("wpf")] + [InlineData("winforms")] + public void ItCanBuildDesktopTemplates(string templateName) => TestTemplateCreateAndBuild(templateName); + + [WindowsOnlyTheory] + [InlineData("wpf")] + public void ItCanBuildDesktopTemplatesSelfContained(string templateName) => TestTemplateCreateAndBuild(templateName, selfContained: true); + + [Theory] + [InlineData("web")] + [InlineData("console")] + public void ItCanBuildTemplatesSelfContained(string templateName) => TestTemplateCreateAndBuild(templateName, selfContained: true); + + /// + /// The test checks if the template creates the template for correct framework by default. + /// For .NET 6 the templates should create the projects targeting net6.0 + /// + [Theory] + [InlineData("console")] + [InlineData("console", "C#")] + [InlineData("console", "VB")] + [InlineData("console", "F#")] + [InlineData("classlib")] + [InlineData("classlib", "C#")] + [InlineData("classlib", "VB")] + [InlineData("classlib", "F#")] + [InlineData("worker")] + [InlineData("worker", "C#")] + [InlineData("worker", "F#")] + [InlineData("mstest")] + [InlineData("mstest", "C#")] + [InlineData("mstest", "VB")] + [InlineData("mstest", "F#")] + [InlineData("nunit")] + [InlineData("nunit", "C#")] + [InlineData("nunit", "VB")] + [InlineData("nunit", "F#")] + [InlineData("xunit")] + [InlineData("xunit", "C#")] + [InlineData("xunit", "VB")] + [InlineData("xunit", "F#")] + [InlineData("blazorwasm")] + [InlineData("web")] + [InlineData("web", "C#")] + [InlineData("web", "F#")] + [InlineData("mvc")] + [InlineData("mvc", "C#")] + [InlineData("mvc", "F#")] + [InlineData("webapi")] + [InlineData("webapi", "C#")] + [InlineData("webapi", "F#")] + [InlineData("webapp")] + [InlineData("razorclasslib")] + public void ItCanCreateAndBuildTemplatesWithDefaultFramework(string templateName, string language = "") + { + string framework = DetectExpectedDefaultFramework(templateName); + TestTemplateCreateAndBuild(templateName, selfContained: false, language: language, framework: framework); + } + + /// + /// [Windows only tests] + /// The test checks if the template creates the template for correct framework by default. + /// For .NET 6 the templates should create the projects targeting net6.0. + /// + [WindowsOnlyTheory] + [InlineData("wpf")] + [InlineData("wpf", "C#")] + [InlineData("wpf", "VB")] + [InlineData("wpflib")] + [InlineData("wpflib", "C#")] + [InlineData("wpflib", "VB")] + [InlineData("wpfcustomcontrollib")] + [InlineData("wpfcustomcontrollib", "C#")] + [InlineData("wpfcustomcontrollib", "VB")] + [InlineData("wpfusercontrollib")] + [InlineData("wpfusercontrollib", "C#")] + [InlineData("wpfusercontrollib", "VB")] + [InlineData("winforms")] + [InlineData("winforms", "C#")] + [InlineData("winforms", "VB")] + [InlineData("winformslib")] + [InlineData("winformslib", "C#")] + [InlineData("winformslib", "VB")] + [InlineData("winformscontrollib")] + [InlineData("winformscontrollib", "C#")] + [InlineData("winformscontrollib", "VB")] + public void ItCanCreateAndBuildTemplatesWithDefaultFramework_Windows(string templateName, string language = "") + { + string framework = DetectExpectedDefaultFramework(templateName); + TestTemplateCreateAndBuild(templateName, selfContained: false, language: language, framework: $"{framework}-windows"); + } + + /// + /// [project is not built on linux-musl] + /// The test checks if the template creates the template for correct framework by default. + /// For .NET 6 the templates should create the projects targeting net6.0. + /// + [Theory] + [InlineData("grpc")] + public void ItCanCreateAndBuildTemplatesWithDefaultFramework_DisableBuildOnLinuxMusl(string templateName) + { + string framework = DetectExpectedDefaultFramework(templateName); + + if (RuntimeInformation.RuntimeIdentifier.StartsWith("linux-musl")) + { + TestTemplateCreateAndBuild(templateName, build: false, framework: framework); + } + else + { + TestTemplateCreateAndBuild(templateName, selfContained: true, framework: framework); + } + } + + private static string DetectExpectedDefaultFramework(string template = "") + { + string dotnetFolder = Path.GetDirectoryName(RepoDirectoriesProvider.DotnetUnderTest); + string[] runtimeFolders = Directory.GetDirectories(Path.Combine(dotnetFolder, "shared", "Microsoft.NETCore.App")); + + int latestMajorVersion = runtimeFolders.Select(folder => int.Parse(Path.GetFileName(folder).Split('.').First())).Max(); + if (latestMajorVersion == 9) + { + return $"net{latestMajorVersion}.0"; + } + + throw new Exception("Unsupported version of SDK"); + } + + private static void TestTemplateCreateAndBuild(string templateName, bool build = true, bool selfContained = false, string language = "", string framework = "", bool deleteTestDirectory = false) + { + DirectoryInfo directory = InstantiateProjectTemplate(templateName, language); + string projectDirectory = directory.FullName; + + if (!string.IsNullOrWhiteSpace(framework)) + { + //check if MSBuild TargetFramework property for *proj is set to expected framework + string expectedExtension = language switch + { + "C#" => "*.csproj", + "F#" => "*.fsproj", + "VB" => "*.vbproj", + _ => "*.csproj" + }; + string projectFile = Directory.GetFiles(projectDirectory, expectedExtension).Single(); + XDocument projectXml = XDocument.Load(projectFile); + XNamespace ns = projectXml.Root.Name.Namespace; + Assert.Equal(framework, projectXml.Root.Element(ns + "PropertyGroup").Element(ns + "TargetFramework").Value); + } + + if (build) + { + string buildArgs = selfContained ? $"-r {RuntimeInformation.RuntimeIdentifier} --self-contained" : ""; + if (!string.IsNullOrWhiteSpace(framework)) + { + buildArgs += $" --framework {framework}"; + } + + // Remove this (or formalize it) after https://github.com/dotnet/installer/issues/12479 is resolved. + if (language == "F#") + { + buildArgs += $" /p:_NETCoreSdkIsPreview=true"; + } + + string dotnetRoot = Path.GetDirectoryName(RepoDirectoriesProvider.DotnetUnderTest); + new BuildCommand() + .WithEnvironmentVariable("PATH", dotnetRoot) // override PATH since razor rely on PATH to find dotnet + .WithWorkingDirectory(projectDirectory) + .Execute(buildArgs) + .Should().Pass(); + } + + // delete test directory for some tests so we aren't leaving behind non-compliant package files + if (deleteTestDirectory) + { + directory.Delete(true); + } + } + + private static DirectoryInfo InstantiateProjectTemplate(string templateName, string language = "", bool withNoRestore = true) + { + DirectoryInfo directory = TestAssets.CreateTestDirectory( + identifier: string.IsNullOrWhiteSpace(language) + ? templateName + : $"{templateName}[{language}]"); + string projectDirectory = directory.FullName; + + string newArgs = $"{templateName} --debug:ephemeral-hive {(withNoRestore ? "--no-restore" : "")}"; + if (!string.IsNullOrWhiteSpace(language)) + { + newArgs += $" --language {language}"; + } + + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute(newArgs) + .Should().Pass(); + + return directory; + } + } +} diff --git a/test/EndToEnd/SupportedNetCoreAppVersions.cs b/test/EndToEnd/SupportedNetCoreAppVersions.cs new file mode 100644 index 000000000..3f1ab1e9c --- /dev/null +++ b/test/EndToEnd/SupportedNetCoreAppVersions.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace EndToEnd +{ + public static class TargetFrameworkHelper + { + private static Version _firstNetAppVersion = new Version(5, 0); + + public static IEnumerable GetNetAppTargetFrameworks(IEnumerable versions) => + versions.Select(v => $"netcoreapp{v}") + // Add netX.X tfms starting with 5.0 + .Concat(versions.Where(v => Version.Parse(v) >= _firstNetAppVersion).Select(v => $"net{v}")); + } + + public class SupportedNetCoreAppVersions : IEnumerable + { + public IEnumerator GetEnumerator() => Versions.Select(version => new object[] { version }).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public static IEnumerable Versions => new[] + { + "1.0", + "1.1", + "2.0", + "2.1", + "2.2", + "3.0", + "3.1", + "5.0", + "6.0", + "7.0", + "8.0", + "9.0" + }; + + public static IEnumerable TargetFrameworkShortFolderVersion + { + get + { + var targetFrameworkShortFolderVersion = new List(); + foreach (var v in Versions) + { + if (Version.Parse(v).Major >= 5) + { + targetFrameworkShortFolderVersion.Add($"net{v}"); + } + else + { + targetFrameworkShortFolderVersion.Add($"netcoreapp{v}"); + } + } + + return targetFrameworkShortFolderVersion; + } + } + } + + public class SupportedAspNetCoreVersions : IEnumerable + { + public IEnumerator GetEnumerator() => Versions.Select(version => new object[] { version }).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public static IEnumerable Versions => + SupportedNetCoreAppVersions.Versions.Except(new List() { "1.0", "1.1", "2.0" }); + } + + public class SupportedAspNetCoreAllVersions : IEnumerable + { + public IEnumerator GetEnumerator() => Versions.Select(version => new object[] { version }).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public static IEnumerable Versions => + SupportedAspNetCoreVersions.Versions.Where(v => new Version(v).Major < 3); + } +} + diff --git a/test/EndToEnd/TelemetryOptOutDefaultTests.cs b/test/EndToEnd/TelemetryOptOutDefaultTests.cs new file mode 100644 index 000000000..1e2d2ecef --- /dev/null +++ b/test/EndToEnd/TelemetryOptOutDefaultTests.cs @@ -0,0 +1,24 @@ +using System.IO; +using FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; + +namespace EndToEnd.Tests +{ + public class TelemetryOptOutDefault : TestBase + { + [Fact] + public void TelemetryOptOutDefaultAttribute() + { + var versionCommand = new DotnetCommand() + .ExecuteWithCapturedOutput("--version"); + + var sdkVersion = versionCommand.StdOut.Trim(); + + var dotnetdir = Path.Combine(Path.GetDirectoryName(RepoDirectoriesProvider.DotnetUnderTest), "sdk", sdkVersion); + + var result = AssemblyInfo.Get(Path.Combine(dotnetdir, "dotnet.dll"), "AssemblyMetadataAttribute"); + result.Should().Contain("TelemetryOptOutDefault:False"); + } + } +} diff --git a/test/EndToEnd/TestProjectCreator.cs b/test/EndToEnd/TestProjectCreator.cs new file mode 100644 index 000000000..8e1eb339a --- /dev/null +++ b/test/EndToEnd/TestProjectCreator.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Xml.Linq; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; + +namespace EndToEnd +{ + class TestProjectCreator + { + public const string NETCorePackageName = "Microsoft.NETCore.App"; + public const string AspNetCoreAppPackageName = "Microsoft.AspNetCore.App"; + public const string AspNetCoreAllPackageName = "Microsoft.AspNetCore.All"; + + + public string TestName { get; set; } + public string Identifier { get; set; } + + public string PackageName { get; set; } = NETCorePackageName; + + public string MinorVersion { get; set; } + + public string RuntimeIdentifier { get; set; } + + public Dictionary AdditionalProperties { get; } = new Dictionary(); + + public TestProjectCreator([CallerMemberName] string testName = null, string identifier = "") + { + TestName = testName; + Identifier = identifier; + } + + public TestAssetInstance Create() + { + var testInstance = TestBase.TestAssets.Get("TestAppSimple") + .CreateInstance(callingMethod: TestName, identifier: Identifier + PackageName + "_" + MinorVersion) + .WithSourceFiles(); + + string projectDirectory = testInstance.Root.FullName; + + string projectPath = Path.Combine(projectDirectory, "TestAppSimple.csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + // Update TargetFramework to the right version of .NET Core + project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework") + .Value = "netcoreapp" + MinorVersion; + + if (!string.IsNullOrEmpty(RuntimeIdentifier)) + { + project.Root.Element(ns + "PropertyGroup") + .Add(new XElement(ns + "RuntimeIdentifier", RuntimeIdentifier)); + } + + foreach (var additionalProperty in AdditionalProperties) + { + project.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + additionalProperty.Key, additionalProperty.Value)); + } + + if (PackageName != NETCorePackageName) + { + if (new Version(MinorVersion).Major < 3) + { + // Add ASP.NET PackageReference with implicit version for target framework versions prior to 3.0 + project.Root.Add(new XElement(ns + "ItemGroup", + new XElement(ns + "PackageReference", new XAttribute("Include", PackageName)))); + } + else + { + project.Root.Add(new XElement(ns + "ItemGroup", + new XElement(ns + "FrameworkReference", new XAttribute("Include", PackageName)))); + } + } + + project.Save(projectPath); + + return testInstance; + } + } +} diff --git a/test/EndToEnd/ValidateInsertedManifests.cs b/test/EndToEnd/ValidateInsertedManifests.cs new file mode 100644 index 000000000..778c07ea7 --- /dev/null +++ b/test/EndToEnd/ValidateInsertedManifests.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Linq; +using FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; +using Microsoft.NET.Sdk.WorkloadManifestReader; +using Xunit; +using Xunit.Abstractions; + +namespace EndToEnd.Tests +{ + public class ValidateInsertedManifests : TestBase + { + private readonly ITestOutputHelper output; + + public ValidateInsertedManifests(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void ManifestReaderCanReadManifests() + { + var sdkManifestDir = Path.Combine(Path.GetDirectoryName(RepoDirectoriesProvider.DotnetUnderTest), "sdk-manifests"); + var sdkversionDir = new DirectoryInfo(sdkManifestDir).EnumerateDirectories().First(); + foreach (var manifestVersionDir in sdkversionDir.EnumerateDirectories()) + { + foreach (var manifestDir in manifestVersionDir.EnumerateDirectories()) + { + var manifestId = manifestVersionDir.Name; + + string manifestFile = manifestDir.GetFile("WorkloadManifest.json").FullName; + + if (!string.Equals(manifestId, "workloadsets")) + { + new FileInfo(manifestFile).Exists.Should().BeTrue(); + using var fileStream = new FileStream(manifestFile, FileMode.Open, FileAccess.Read); + Action readManifest = () => WorkloadManifestReader.ReadWorkloadManifest(manifestId, fileStream, manifestFile); + readManifest.ShouldNotThrow("manifestId:" + manifestId + " manifestFile:" + manifestFile + "is invalid"); + } + } + } + + } + } +} diff --git a/test/EndToEnd/VersionTests.cs b/test/EndToEnd/VersionTests.cs new file mode 100644 index 000000000..9c73efdfb --- /dev/null +++ b/test/EndToEnd/VersionTests.cs @@ -0,0 +1,30 @@ +using System.IO; +using System.Linq; +using FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; + +namespace EndToEnd.Tests +{ + public class VersionTests : TestBase + { + [Fact] + public void DotnetVersionReturnsCorrectVersion() + { + var result = new DotnetCommand() + .ExecuteWithCapturedOutput("--version"); + + result.Should().Pass(); + + var dotnetFolder = Path.GetDirectoryName(RepoDirectoriesProvider.DotnetUnderTest); + + var sdkFolders = Directory.GetDirectories(Path.Combine(dotnetFolder, "sdk")); + + sdkFolders.Length.Should().Be(1, "Only one SDK folder is expected in the layout"); + + var expectedSdkVersion = Path.GetFileName(sdkFolders.Single()); + + result.StdOut.Trim().Should().Be(expectedSdkVersion); + } + } +} diff --git a/test/EndToEnd/xunit.runner.json b/test/EndToEnd/xunit.runner.json new file mode 100644 index 000000000..529c2a6d8 --- /dev/null +++ b/test/EndToEnd/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "shadowCopy": false, + "methodDisplay": "method", + "diagnosticMessages": true, + "longRunningTestSeconds": 120 +} diff --git a/test/Microsoft.DotNet.Cli.Tests.sln b/test/Microsoft.DotNet.Cli.Tests.sln new file mode 100644 index 000000000..d269ba06b --- /dev/null +++ b/test/Microsoft.DotNet.Cli.Tests.sln @@ -0,0 +1,64 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2024 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.TestFramework", "Microsoft.DotNet.TestFramework\Microsoft.DotNet.TestFramework.csproj", "{BEA33DFA-8559-4680-B40A-B522FBF37567}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Tools.Tests.Utilities", "Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj", "{770FE8B9-6381-4328-AA36-D6A21114701E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EndToEnd", "EndToEnd\EndToEnd.csproj", "{AEB7B217-D4C4-46F1-9495-87F869F0ABE4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Debug|x64.ActiveCfg = Debug|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Debug|x64.Build.0 = Debug|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Debug|x86.ActiveCfg = Debug|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Debug|x86.Build.0 = Debug|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Release|Any CPU.Build.0 = Release|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Release|x64.ActiveCfg = Release|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Release|x64.Build.0 = Release|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Release|x86.ActiveCfg = Release|Any CPU + {BEA33DFA-8559-4680-B40A-B522FBF37567}.Release|x86.Build.0 = Release|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Debug|x64.ActiveCfg = Debug|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Debug|x64.Build.0 = Debug|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Debug|x86.ActiveCfg = Debug|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Debug|x86.Build.0 = Debug|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Release|Any CPU.Build.0 = Release|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Release|x64.ActiveCfg = Release|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Release|x64.Build.0 = Release|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Release|x86.ActiveCfg = Release|Any CPU + {770FE8B9-6381-4328-AA36-D6A21114701E}.Release|x86.Build.0 = Release|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Debug|x64.ActiveCfg = Debug|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Debug|x64.Build.0 = Debug|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Debug|x86.ActiveCfg = Debug|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Debug|x86.Build.0 = Debug|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Release|Any CPU.Build.0 = Release|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Release|x64.ActiveCfg = Release|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Release|x64.Build.0 = Release|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Release|x86.ActiveCfg = Release|Any CPU + {AEB7B217-D4C4-46F1-9495-87F869F0ABE4}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7AF6777A-0133-453A-B302-FDD974B8F39C} + EndGlobalSection +EndGlobal diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/AssemblyInfo.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/AssemblyInfo.cs new file mode 100644 index 000000000..f02cfbeb3 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/AssemblyInfo.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Text; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static class AssemblyInfo + { + public static IList Get(string assemblyPath, string assemblyInfoName) + { + var list = new List(); + + using (var stream = File.OpenRead(assemblyPath)) + using (var peReader = new PEReader(stream)) + { + var metadataReader = peReader.GetMetadataReader(); + var assemblyDefinition = metadataReader.GetAssemblyDefinition(); + + foreach (var handle in assemblyDefinition.GetCustomAttributes()) + { + var attribute = metadataReader.GetCustomAttribute(handle); + var constructor = metadataReader.GetMemberReference((MemberReferenceHandle)attribute.Constructor); + var type = metadataReader.GetTypeReference((TypeReferenceHandle)constructor.Parent); + var name = metadataReader.GetString(type.Name); + + if (name.Equals(assemblyInfoName)) + { + var signature = metadataReader.GetBlobReader(constructor.Signature); + var value = metadataReader.GetBlobReader(attribute.Value); + var header = signature.ReadSignatureHeader(); + + const ushort prolog = 1; // two-byte "prolog" defined by ECMA-335 (II.23.3) to be at the beginning of attribute value blobs + if (value.ReadUInt16() != prolog || header.Kind != SignatureKind.Method || header.IsGeneric) + { + throw new BadImageFormatException(); + } + + var paramCount = signature.ReadCompressedInteger(); + if (paramCount <= 0 || // must have at least 1 parameter + signature.ReadSignatureTypeCode() != SignatureTypeCode.Void) // return type must be void + { + continue; + } + + var sb = new StringBuilder(); + while (paramCount > 0 && sb != null) + { + switch (signature.ReadSignatureTypeCode()) + { + case SignatureTypeCode.String: + sb.Append(value.ReadSerializedString()); + break; + default: + sb = null; + break; + } + + paramCount--; + if (paramCount != 0) + { + sb?.Append(':'); + } + } + + if (sb != null) + { + list.Add(sb.ToString()); + } + } + } + } + return list; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultAssertions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultAssertions.cs new file mode 100644 index 000000000..7301d9a81 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultAssertions.cs @@ -0,0 +1,164 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Text.RegularExpressions; +using FluentAssertions; +using FluentAssertions.Execution; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class CommandResultAssertions + { + private CommandResult _commandResult; + + public CommandResultAssertions(CommandResult commandResult) + { + _commandResult = commandResult; + } + + public AndConstraint ExitWith(int expectedExitCode) + { + Execute.Assertion.ForCondition(_commandResult.ExitCode == expectedExitCode) + .FailWith(AppendDiagnosticsTo($"Expected command to exit with {expectedExitCode} but it did not.")); + return new AndConstraint(this); + } + + public AndConstraint Pass() + { + Execute.Assertion.ForCondition(_commandResult.ExitCode == 0) + .FailWith(AppendDiagnosticsTo($"Expected command to pass but it did not.")); + return new AndConstraint(this); + } + + public AndConstraint Fail() + { + Execute.Assertion.ForCondition(_commandResult.ExitCode != 0) + .FailWith(AppendDiagnosticsTo($"Expected command to fail but it did not.")); + return new AndConstraint(this); + } + + public AndConstraint HaveStdOut() + { + Execute.Assertion.ForCondition(!string.IsNullOrEmpty(_commandResult.StdOut)) + .FailWith(AppendDiagnosticsTo("Command did not output anything to stdout")); + return new AndConstraint(this); + } + + public AndConstraint HaveStdOut(string expectedOutput) + { + Execute.Assertion.ForCondition(_commandResult.StdOut.Equals(expectedOutput, StringComparison.Ordinal)) + .FailWith(AppendDiagnosticsTo($"Command did not output with Expected Output. Expected: {expectedOutput}")); + return new AndConstraint(this); + } + + public AndConstraint HaveStdOutContaining(string pattern) + { + Execute.Assertion + .ForCondition(_commandResult.StdOut.Contains(pattern)) + .FailWith(AppendDiagnosticsTo($"The command output did not contain expected result: {pattern}{Environment.NewLine}")); + + return new AndConstraint(this); + } + + public AndConstraint HaveStdOutContainingIgnoreSpaces(string pattern) + { + string commandResultNoSpaces = _commandResult.StdOut.Replace(" ", ""); + + Execute.Assertion + .ForCondition(commandResultNoSpaces.Contains(pattern)) + .FailWith(AppendDiagnosticsTo($"The command output did not contain expected result: {pattern}{Environment.NewLine}")); + + return new AndConstraint(this); + } + + public AndConstraint HaveStdOutContainingIgnoreCase(string pattern) + { + Execute.Assertion.ForCondition(_commandResult.StdOut.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) >= 0) + .FailWith(AppendDiagnosticsTo($"The command output did not contain expected result (ignoring case): {pattern}{Environment.NewLine}")); + return new AndConstraint(this); + } + + public AndConstraint NotHaveStdOutContaining(string pattern) + { + Execute.Assertion.ForCondition(!_commandResult.StdOut.Contains(pattern)) + .FailWith(AppendDiagnosticsTo($"The command output contained a result it should not have contained: {pattern}{Environment.NewLine}")); + return new AndConstraint(this); + } + + public AndConstraint HaveStdOutMatching(string pattern, RegexOptions options = RegexOptions.None) + { + Execute.Assertion.ForCondition(Regex.Match(_commandResult.StdOut, pattern, options).Success) + .FailWith(AppendDiagnosticsTo($"Matching the command output failed. Pattern: {pattern}{Environment.NewLine}")); + return new AndConstraint(this); + } + + public AndConstraint HaveStdErr() + { + Execute.Assertion.ForCondition(!string.IsNullOrEmpty(_commandResult.StdErr)) + .FailWith(AppendDiagnosticsTo("Command did not output anything to stderr.")); + return new AndConstraint(this); + } + + public AndConstraint HaveStdErrContaining(string pattern) + { + Execute.Assertion.ForCondition(_commandResult.StdErr.Contains(pattern)) + .FailWith(AppendDiagnosticsTo($"The command error output did not contain expected result: {pattern}{Environment.NewLine}")); + return new AndConstraint(this); + } + + public AndConstraint NotHaveStdErrContaining(string pattern) + { + Execute.Assertion.ForCondition(!_commandResult.StdErr.Contains(pattern)) + .FailWith(AppendDiagnosticsTo($"The command error output contained a result it should not have contained: {pattern}{Environment.NewLine}")); + return new AndConstraint(this); + } + + public AndConstraint HaveStdErrMatching(string pattern, RegexOptions options = RegexOptions.None) + { + Execute.Assertion.ForCondition(Regex.Match(_commandResult.StdErr, pattern, options).Success) + .FailWith(AppendDiagnosticsTo($"Matching the command error output failed. Pattern: {pattern}{Environment.NewLine}")); + return new AndConstraint(this); + } + + public AndConstraint NotHaveStdOut() + { + Execute.Assertion.ForCondition(string.IsNullOrEmpty(_commandResult.StdOut)) + .FailWith(AppendDiagnosticsTo($"Expected command to not output to stdout but it was not:")); + return new AndConstraint(this); + } + + public AndConstraint NotHaveStdErr() + { + Execute.Assertion.ForCondition(string.IsNullOrEmpty(_commandResult.StdErr)) + .FailWith(AppendDiagnosticsTo("Expected command to not output to stderr but it was not:")); + return new AndConstraint(this); + } + + private string AppendDiagnosticsTo(string s) + { + return s + $"{Environment.NewLine}" + + $"File Name: {_commandResult.StartInfo.FileName}{Environment.NewLine}" + + $"Arguments: {_commandResult.StartInfo.Arguments}{Environment.NewLine}" + + $"WorkingDir:: {_commandResult.StartInfo.WorkingDirectory}{Environment.NewLine}" + + $"Exit Code: {_commandResult.ExitCode}{Environment.NewLine}" + + $"StdOut:{Environment.NewLine}{_commandResult.StdOut}{Environment.NewLine}" + + $"StdErr:{Environment.NewLine}{_commandResult.StdErr}{Environment.NewLine}"; ; + } + + public AndConstraint HaveSkippedProjectCompilation(string skippedProject, string frameworkFullName) + { + _commandResult.StdOut.Should().Contain($"Project {skippedProject} ({frameworkFullName}) was previously compiled. Skipping compilation."); + + return new AndConstraint(this); + } + + public AndConstraint HaveCompiledProject(string compiledProject, string frameworkFullName) + { + _commandResult.StdOut.Should().Contain($"Project {compiledProject} ({frameworkFullName}) will be compiled"); + + return new AndConstraint(this); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultExtensions.cs new file mode 100644 index 000000000..4cd8b0cfe --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultExtensions.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static class CommandResultExtensions + { + public static CommandResultAssertions Should(this CommandResult commandResult) => new CommandResultAssertions(commandResult); + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/DirectoryInfoAssertions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/DirectoryInfoAssertions.cs new file mode 100644 index 000000000..f17a4bc30 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/DirectoryInfoAssertions.cs @@ -0,0 +1,218 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using FluentAssertions; +using FluentAssertions.Execution; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class DirectoryInfoAssertions + { + private DirectoryInfo _dirInfo; + + public DirectoryInfoAssertions(DirectoryInfo dir) + { + _dirInfo = dir; + } + + public DirectoryInfo DirectoryInfo => _dirInfo; + + public AndConstraint Exist() + { + Execute.Assertion.ForCondition(_dirInfo.Exists) + .FailWith("Expected directory {0} does not exist.", _dirInfo.FullName); + return new AndConstraint(this); + } + + public AndConstraint HaveFile(string expectedFile, string because = "", params object[] reasonArgs) + { + var file = _dirInfo.EnumerateFiles(expectedFile, SearchOption.TopDirectoryOnly).SingleOrDefault(); + + Execute.Assertion + .ForCondition(file != null) + .BecauseOf(because, reasonArgs) + .FailWith($"Expected File {expectedFile} cannot be found in directory {_dirInfo.FullName}."); + + return new AndConstraint(this); + } + + public AndConstraint HaveTextFile( + string expectedFile, + string expectedContents, + string because = "", + params object[] reasonArgs) + { + this.HaveFile(expectedFile, because, reasonArgs); + + var file = _dirInfo.EnumerateFiles(expectedFile, SearchOption.TopDirectoryOnly).SingleOrDefault(); + + var contents = File.ReadAllText(file.FullName); + + Execute.Assertion + .ForCondition(contents.Equals(expectedContents)) + .BecauseOf(because, reasonArgs) + .FailWith($"Expected file {expectedFile} to contain \n\n{expectedContents}\n\nbut it contains\n\n{contents}\n"); + + return new AndConstraint(this); + } + + public AndConstraint NotHaveFile( + string expectedFile, + string because = "", + params object [] reasonArgs) + { + var file = _dirInfo.EnumerateFiles(expectedFile, SearchOption.TopDirectoryOnly).SingleOrDefault(); + Execute.Assertion + .ForCondition(file == null) + .BecauseOf(because, reasonArgs) + .FailWith("File {0} should not be found in directory {1}.", expectedFile, _dirInfo.FullName); + return new AndConstraint(this); + } + + public AndConstraint HaveFiles(IEnumerable expectedFiles) + { + foreach (var expectedFile in expectedFiles) + { + HaveFile(expectedFile); + } + + return new AndConstraint(this); + } + + public AndConstraint HaveTextFiles( + IDictionary expectedFiles, + string because = "", + params object[] reasonArgs) + { + foreach (var expectedFile in expectedFiles) + { + HaveTextFile(expectedFile.Key, expectedFile.Value, because, reasonArgs); + } + + return new AndConstraint(this); + } + + public AndConstraint HaveFilesMatching( + string expectedFilesSearchPattern, + SearchOption searchOption, + string because = "", + params object[] reasonArgs) + { + var matchingFileExists = _dirInfo.EnumerateFiles(expectedFilesSearchPattern, searchOption).Any(); + + Execute.Assertion + .ForCondition(matchingFileExists == true) + .BecauseOf(because, reasonArgs) + .FailWith("Expected directory {0} to contain files matching {1}, but no matching file exists.", + _dirInfo.FullName, expectedFilesSearchPattern); + + return new AndConstraint(this); + } + + public AndConstraint NotHaveFiles( + IEnumerable expectedFiles, + string because = "", + params object [] reasonArgs) + { + foreach (var expectedFile in expectedFiles) + { + NotHaveFile(expectedFile, because, reasonArgs); + } + + return new AndConstraint(this); + } + + public AndConstraint NotHaveFilesMatching(string expectedFilesSearchPattern, SearchOption searchOption) + { + var matchingFileCount = _dirInfo.EnumerateFiles(expectedFilesSearchPattern, searchOption).Count(); + Execute.Assertion.ForCondition(matchingFileCount == 0) + .FailWith("Found {0} files that should not exist in directory {1}. No file matching {2} should exist.", + matchingFileCount, _dirInfo.FullName, expectedFilesSearchPattern); + return new AndConstraint(this); + } + + public AndConstraint HaveDirectory(string expectedDir) + { + var dir = _dirInfo.EnumerateDirectories(expectedDir, SearchOption.TopDirectoryOnly).SingleOrDefault(); + Execute.Assertion.ForCondition(dir != null) + .FailWith("Expected directory {0} cannot be found inside directory {1}.", expectedDir, _dirInfo.FullName); + + return new AndConstraint(new DirectoryInfoAssertions(dir)); + } + + public AndConstraint HaveDirectories(IEnumerable expectedDirs) + { + foreach (var expectedDir in expectedDirs) + { + HaveDirectory(expectedDir); + } + + return new AndConstraint(this); + } + + public AndConstraint NotHaveDirectory(string unexpectedDir) + { + var dir = _dirInfo.EnumerateDirectories(unexpectedDir, SearchOption.TopDirectoryOnly).SingleOrDefault(); + Execute.Assertion.ForCondition(dir == null) + .FailWith("Directory {0} should not be found in directory {1}.", unexpectedDir, _dirInfo.FullName); + + return new AndConstraint(new DirectoryInfoAssertions(dir)); + } + + public AndConstraint NotHaveDirectories(IEnumerable unexpectedDirs) + { + foreach (var unexpectedDir in unexpectedDirs) + { + NotHaveDirectory(unexpectedDir); + } + + return new AndConstraint(this); + } + + public AndConstraint OnlyHaveFiles(IEnumerable expectedFiles) + { + var actualFiles = _dirInfo.EnumerateFiles("*", SearchOption.TopDirectoryOnly).Select(f => f.Name); + var missingFiles = Enumerable.Except(expectedFiles, actualFiles); + var extraFiles = Enumerable.Except(actualFiles, expectedFiles); + var nl = Environment.NewLine; + + Execute.Assertion.ForCondition(!missingFiles.Any()) + .FailWith($"Following files cannot be found inside directory {_dirInfo.FullName} {nl} {string.Join(nl, missingFiles)}"); + + Execute.Assertion.ForCondition(!extraFiles.Any()) + .FailWith($"Following extra files are found inside directory {_dirInfo.FullName} {nl} {string.Join(nl, extraFiles)}"); + + return new AndConstraint(this); + } + + public AndConstraint BeEmpty() + { + Execute.Assertion.ForCondition(!_dirInfo.EnumerateFileSystemInfos().Any()) + .FailWith($"The directory {_dirInfo.FullName} is not empty."); + + return new AndConstraint(this); + } + + public AndConstraint NotBeEmpty() + { + Execute.Assertion.ForCondition(_dirInfo.EnumerateFileSystemInfos().Any()) + .FailWith($"The directory {_dirInfo.FullName} is empty."); + + return new AndConstraint(this); + } + + public AndConstraint NotExist(string because = "", params object[] reasonArgs) + { + Execute.Assertion + .ForCondition(_dirInfo.Exists == false) + .BecauseOf(because, reasonArgs) + .FailWith($"Expected directory {_dirInfo.FullName} to not exist, but it does."); + + return new AndConstraint(this); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/DirectoryInfoExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/DirectoryInfoExtensions.cs new file mode 100644 index 000000000..e5dc6418e --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/DirectoryInfoExtensions.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static class DirectoryInfoExtensions + { + public static DirectoryInfoAssertions Should(this DirectoryInfo dir) => new DirectoryInfoAssertions(dir); + + public static DirectoryInfo Sub(this DirectoryInfo dir, string name) => new DirectoryInfo(Path.Combine(dir.FullName, name)); + + public static bool Contains(this DirectoryInfo subject, FileSystemInfo target) => target.FullName.StartsWith(subject.FullName); + + public static DirectoryInfo GetDirectory(this DirectoryInfo subject, params string[] directoryNames) => new DirectoryInfo(Path.Combine(subject.FullName, Path.Combine(directoryNames))); + + public static FileInfo GetFile(this DirectoryInfo subject, string fileName) => new FileInfo(Path.Combine(subject.FullName, fileName)); + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoAssertions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoAssertions.cs new file mode 100644 index 000000000..49f2f0037 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoAssertions.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using FluentAssertions; +using FluentAssertions.Execution; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class FileInfoAssertions + { + private FileInfo _fileInfo; + + public FileInfoAssertions(FileInfo file) + { + _fileInfo = file; + } + + public FileInfo FileInfo => _fileInfo; + + public AndConstraint Exist(string because = "", params object[] reasonArgs) + { + Execute.Assertion + .ForCondition(_fileInfo.Exists) + .BecauseOf(because, reasonArgs) + .FailWith($"Expected File {_fileInfo.FullName} to exist, but it does not."); + return new AndConstraint(this); + } + + public AndConstraint NotExist(string because = "", params object[] reasonArgs) + { + Execute.Assertion + .ForCondition(!_fileInfo.Exists) + .BecauseOf(because, reasonArgs) + .FailWith($"Expected File {_fileInfo.FullName} to not exist, but it does."); + return new AndConstraint(this); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.FileInfoLock.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.FileInfoLock.cs new file mode 100644 index 000000000..af9428fec --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.FileInfoLock.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static partial class FileInfoExtensions + { + private class FileInfoLock : IDisposable + { + private FileStream _fileStream; + + public FileInfoLock(FileInfo fileInfo) + { + _fileStream = fileInfo.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); + } + + public void Dispose() => _fileStream.Dispose(); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.FileInfoNuGetLock.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.FileInfoNuGetLock.cs new file mode 100644 index 000000000..d0fd7d3ee --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.FileInfoNuGetLock.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Common; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static partial class FileInfoExtensions + { + private class FileInfoNuGetLock : IDisposable + { + private CancellationTokenSource _cancellationTokenSource; + + private Task _task; + + public FileInfoNuGetLock(FileInfo fileInfo) + { + var taskCompletionSource = new TaskCompletionSource(); + + _cancellationTokenSource = new CancellationTokenSource(); + + _task = Task.Run(async () => await ConcurrencyUtilities.ExecuteWithFileLockedAsync( + fileInfo.FullName, + cancellationToken => + { + taskCompletionSource.SetResult("Lock is taken so test can continue"); + + Task.Delay(60000, cancellationToken).Wait(); + + return Task.FromResult(0); + }, + _cancellationTokenSource.Token)); + + taskCompletionSource.Task.Wait(); + } + + public void Dispose() + { + _cancellationTokenSource.Cancel(); + + try + { + _task.Wait(); + } + catch (AggregateException) + { + } + finally + { + _cancellationTokenSource.Dispose(); + } + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.cs new file mode 100644 index 000000000..21bae6bbc --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/FileInfoExtensions.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static partial class FileInfoExtensions + { + public static FileInfoAssertions Should(this FileInfo file) => new FileInfoAssertions(file); + + public static IDisposable Lock(this FileInfo subject) => new FileInfoLock(subject); + + public static IDisposable NuGetLock(this FileInfo subject) => new FileInfoNuGetLock(subject); + + public static string ReadAllText(this FileInfo subject) => File.ReadAllText(subject.FullName); + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/StringAssertionsExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/StringAssertionsExtensions.cs new file mode 100644 index 000000000..ad32d79b8 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/StringAssertionsExtensions.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using FluentAssertions; +using FluentAssertions.Execution; +using FluentAssertions.Primitives; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static class StringAssertionsExtensions + { + private static string NormalizeLineEndings(string s) => s.Replace("\r\n", "\n"); + + public static AndConstraint BeVisuallyEquivalentTo(this StringAssertions assertions, string expected, string because = "", params object[] becauseArgs) + { + Execute.Assertion + .ForCondition(NormalizeLineEndings(assertions.Subject) == NormalizeLineEndings(expected)) + .BecauseOf(because, becauseArgs) + .FailWith($"String \"{assertions.Subject}\" is not visually equivalent to expected string \"{expected}\"."); + + return new AndConstraint(assertions); + } + + public static AndConstraint ContainVisuallySameFragment(this StringAssertions assertions, string expected, string because = "", params object[] becauseArgs) + { + Execute.Assertion + .ForCondition(NormalizeLineEndings(assertions.Subject).Contains(NormalizeLineEndings(expected))) + .BecauseOf(because, becauseArgs) + .FailWith($"String \"{assertions.Subject}\" does not contain visually same fragment string \"{expected}\"."); + + return new AndConstraint(assertions); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/BufferedReporter.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/BufferedReporter.cs new file mode 100644 index 000000000..2461e7f6c --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/BufferedReporter.cs @@ -0,0 +1,21 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class BufferedReporter : IReporter + { + public List Lines { get; private set; } = new List(); + + public void WriteLine(string format, params object?[] args) => WriteLine(string.Format(format, args)); + + public void WriteLine(string message) => Lines.Add(message); + + public void WriteLine() => Lines.Add(""); + + public void Write(string message) => throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddReferenceCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddReferenceCommand.cs new file mode 100644 index 000000000..a1d081457 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddReferenceCommand.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class AddReferenceCommand : DotnetCommand + { + private string _projectName = null; + + public override CommandResult Execute(string args = "") + { + args = $"add {_projectName} reference {args}"; + return ExecuteWithCapturedOutput(args); + } + + public AddReferenceCommand WithProject(string projectName) + { + _projectName = projectName; + return this; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs new file mode 100644 index 000000000..86d22bbb1 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs @@ -0,0 +1,185 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using Microsoft.DotNet.Cli.Utils; +using NuGet.Frameworks; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class BuildCommand : DotnetCommand + { + + private bool _captureOutput; + + private string _configuration; + + private string _framework; + + private string _runtime; + + private bool _noDependencies; + + private DirectoryInfo _outputPath; + + private FileInfo _projectFile; + + private DirectoryInfo _workingDirectory; + + public override CommandResult Execute(string args = "") + { + args = $"build {GetNoDependencies()} {GetProjectFile()} {GetOutputPath()} {GetConfiguration()} {GetFramework()} {GetRuntime()} {args}"; + + if (_workingDirectory != null) + { + this.WithWorkingDirectory(_workingDirectory.FullName); + } + + if (_captureOutput) + { + return base.ExecuteWithCapturedOutput(args); + } + else + { + return base.Execute(args); + } + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + WithCapturedOutput(); + + return Execute(args); + } + + public BuildCommand WithCapturedOutput() + { + _captureOutput = true; + + return this; + } + + public BuildCommand WithConfiguration(string configuration) + { + _configuration = configuration; + + return this; + } + + public BuildCommand WithFramework(NuGetFramework framework) + { + _framework = framework.GetShortFolderName(); + + return this; + } + + public BuildCommand WithFramework(string framework) + { + _framework = framework; + + return this; + } + + public BuildCommand WithRuntime(string runtime) + { + _runtime = runtime; + + return this; + } + + public BuildCommand WithNoDependencies() + { + _noDependencies = true; + + return this; + } + + public BuildCommand WithOutputPath(DirectoryInfo outputPath) + { + _outputPath = outputPath; + + return this; + } + + public BuildCommand WithProjectDirectory(DirectoryInfo projectDirectory) + { + _workingDirectory = projectDirectory; + + return this; + } + + public BuildCommand WithProjectFile(FileInfo projectFile) + { + _projectFile = projectFile; + + return this; + } + + public BuildCommand WithWorkingDirectory(DirectoryInfo workingDirectory) + { + _workingDirectory = workingDirectory; + + return this; + } + + private string GetConfiguration() + { + if (_configuration == null) + { + return null; + } + + return $"--configuration {_configuration}"; + } + + private string GetFramework() + { + if (_framework == null) + { + return null; + } + + return $"--framework {_framework}"; + } + + private string GetRuntime() + { + if (_runtime == null) + { + return null; + } + + return $"--runtime {_runtime}"; + } + + private string GetNoDependencies() + { + if (!_noDependencies) + { + return null; + } + + return "--no-dependencies"; + } + + private string GetOutputPath() + { + if (_outputPath == null) + { + return null; + } + + return $"\"{_outputPath.FullName}\""; + } + + private string GetProjectFile() + { + if (_projectFile == null) + { + return null; + } + + return $"\"{_projectFile.FullName}\""; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildServerCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildServerCommand.cs new file mode 100644 index 000000000..463642689 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildServerCommand.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class BuildServerCommand : DotnetCommand + { + public override CommandResult Execute(string args = "") => base.Execute($"build-server {args}"); + + public override CommandResult ExecuteWithCapturedOutput(string args = "") => base.ExecuteWithCapturedOutput($"build-server {args}"); + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/CleanCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/CleanCommand.cs new file mode 100644 index 000000000..e504b88e5 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/CleanCommand.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class CleanCommand : DotnetCommand + { + public override CommandResult Execute(string args = "") + { + args = $"clean {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"clean {args}"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DependencyToolInvokerCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DependencyToolInvokerCommand.cs new file mode 100644 index 000000000..2dc14cae0 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DependencyToolInvokerCommand.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class DependencyToolInvokerCommand : DotnetCommand + { + public DependencyToolInvokerCommand() : base() + { + } + + public DependencyToolInvokerCommand(string dotnetUnderTest) : base(dotnetUnderTest) + { + } + + public CommandResult Execute(string commandName, string framework, string additionalArgs) + { + var args = $"dependency-tool-invoker {commandName} --framework {framework} {additionalArgs}"; + return base.Execute(args); + } + + public CommandResult ExecuteWithCapturedOutput(string commandName, string framework, string additionalArgs) + { + var args = $"dependency-tool-invoker {commandName} --framework {framework} {additionalArgs}"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DotnetCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DotnetCommand.cs new file mode 100644 index 000000000..ca6330006 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DotnetCommand.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class DotnetCommand : TestCommand + { + public DotnetCommand() + : this(RepoDirectoriesProvider.DotnetUnderTest) + { + } + + public DotnetCommand(string dotnetUnderTest) + : base(dotnetUnderTest) + { + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DotnetTestCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DotnetTestCommand.cs new file mode 100644 index 000000000..6e6dd548a --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/DotnetTestCommand.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class DotnetTestCommand : DotnetCommand + { + private string _runtime; + + public override CommandResult Execute(string args = "") + { + args = $"test {GetRuntime()} {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"test {GetRuntime()} {args}"; + return base.ExecuteWithCapturedOutput(args); + } + + public DotnetTestCommand WithRuntime(string runtime) + { + _runtime = runtime; + + return this; + } + + private string GetRuntime() + { + if (_runtime == null) + { + return null; + } + + return $"-property:RuntimeIdentifier={_runtime}"; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/HelpCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/HelpCommand.cs new file mode 100644 index 000000000..a816d97cd --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/HelpCommand.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class HelpCommand : DotnetCommand + { + public override CommandResult Execute(string args = "") => base.Execute(AppendHelp(args)); + + public override CommandResult ExecuteWithCapturedOutput(string args = "") => base.ExecuteWithCapturedOutput(AppendHelp(args)); + + private string AppendHelp(string args) => args = $"help {args}"; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ListPackageCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ListPackageCommand.cs new file mode 100644 index 000000000..6b186966b --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ListPackageCommand.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class ListPackageCommand : DotnetCommand + { + private string _path = null; + + public override CommandResult Execute(string args = "") + { + args = $"list {_path} package {args}"; + return ExecuteWithCapturedOutput(args); + } + + public ListPackageCommand WithPath(string path) + { + _path = path; + return this; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ListReferenceCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ListReferenceCommand.cs new file mode 100644 index 000000000..796a2b9dd --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ListReferenceCommand.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class ListReferenceCommand : DotnetCommand + { + private string _projectName = null; + + public override CommandResult Execute(string args = "") + { + args = $"list {_projectName} reference {args}"; + return ExecuteWithCapturedOutput(args); + } + + public ListReferenceCommand WithProject(string projectName) + { + _projectName = projectName; + return this; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/MSBuildCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/MSBuildCommand.cs new file mode 100644 index 000000000..5f6a184bf --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/MSBuildCommand.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class MSBuildCommand : DotnetCommand + { + public override CommandResult Execute(string args = "") + { + args = $"msbuild {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"msbuild {args}"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/MigrateCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/MigrateCommand.cs new file mode 100644 index 000000000..63398ce7b --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/MigrateCommand.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class MigrateCommand : DotnetCommand + { + public override CommandResult Execute(string args="") + { + args = $"migrate {args}"; + + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"migrate {args}"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/NewCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/NewCommand.cs new file mode 100644 index 000000000..3e0e4d89f --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/NewCommand.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class NewCommand : DotnetCommand + { + public override CommandResult Execute(string args = "") + { + args = $"new {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"new {args}"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/NewCommandShim.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/NewCommandShim.cs new file mode 100644 index 000000000..32441783f --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/NewCommandShim.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class NewCommandShim : DotnetCommand + { + public override CommandResult Execute(string args = "") + { + args = $"new {args} --debug:ephemeral-hive"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"new {args} --debug:ephemeral-hive"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PackCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PackCommand.cs new file mode 100644 index 000000000..495ea0b0b --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PackCommand.cs @@ -0,0 +1,80 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class PackCommand : DotnetCommand + { + private string _projectPath; + private string _outputDirectory; + private string _buildBasePath; + private string _tempOutputDirectory; + private string _configuration; + private string _versionSuffix; + private bool _serviceable; + + private string OutputOption => _outputDirectory == string.Empty ? + "" : + $"-o \"{_outputDirectory}\""; + private string BuildBasePathOption => _buildBasePath == string.Empty ? + "" : + $"-b \"{_buildBasePath}\""; + + private string TempOutputOption => _tempOutputDirectory == string.Empty ? + "" : + $"-t {_tempOutputDirectory}"; + + private string ConfigurationOption => _configuration == string.Empty ? + "" : + $"-c {_configuration}"; + + private string VersionSuffixOption => _versionSuffix == string.Empty ? + "" : + $"--version-suffix {_versionSuffix}"; + + private string ServiceableOption => _serviceable ? + $"--serviceable" : + ""; + + public PackCommand WithConfiguration(string configuration) + { + _configuration = configuration; + + return this; + } + + public PackCommand( + string projectPath = "", + string output = "", + string buildBasePath = "", + string tempOutput="", + string configuration="", + string versionSuffix="", + bool serviceable = false) + { + _projectPath = projectPath; + _outputDirectory = output; + _buildBasePath = buildBasePath; + _tempOutputDirectory = tempOutput; + _configuration = configuration; + _versionSuffix = versionSuffix; + _serviceable = serviceable; + } + + public override CommandResult Execute(string args = "") + { + args = $"pack {BuildArgs()} {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"pack {BuildArgs()} {args}"; + return base.ExecuteWithCapturedOutput(args); + } + + private string BuildArgs() => $"{_projectPath} {OutputOption} {BuildBasePathOption} {TempOutputOption} {ConfigurationOption} {VersionSuffixOption} {ServiceableOption}"; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PublishCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PublishCommand.cs new file mode 100644 index 000000000..fd39bb6bf --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PublishCommand.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; +using NuGet.Frameworks; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class PublishCommand : DotnetCommand + { + private string _framework; + private string _output; + private string _runtime; + private List _targetManifests = new List(); + private bool? _selfContained; + + public PublishCommand WithFramework(string framework) + { + _framework = framework; + return this; + } + + public PublishCommand WithFramework(NuGetFramework framework) => WithFramework(framework.GetShortFolderName()); + + public PublishCommand WithOutput(string output) + { + _output = output; + return this; + } + + public PublishCommand WithRuntime(string runtime) + { + _runtime = runtime; + return this; + } + + public PublishCommand WithTargetManifest(string manifest) + { + _targetManifests.Add( $"--manifest {manifest}"); + return this; + } + + public PublishCommand WithSelfContained(bool value) + { + _selfContained = value; + return this; + } + + public override CommandResult Execute(string args = "") + { + args = $"publish {BuildArgs()} {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"publish {BuildArgs()} {args}"; + return base.ExecuteWithCapturedOutput(args); + } + + private string BuildArgs() => string.Join(" ", + FrameworkOption, + OutputOption, + TargetOption, + RuntimeOption, + SelfContainedOption); + + private string FrameworkOption => string.IsNullOrEmpty(_framework) ? "" : $"-f {_framework}"; + + private string OutputOption => string.IsNullOrEmpty(_output) ? "" : $"-o {_output}"; + + private string RuntimeOption => string.IsNullOrEmpty(_runtime) ? "" : $"-r {_runtime}"; + + private string TargetOption => string.Join(" ", _targetManifests); + + private string SelfContainedOption => _selfContained.HasValue ? $"--self-contained:{_selfContained.Value}" : ""; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RemoveReferenceCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RemoveReferenceCommand.cs new file mode 100644 index 000000000..6e33f1538 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RemoveReferenceCommand.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class RemoveReferenceCommand : DotnetCommand + { + private string _projectName = null; + + public override CommandResult Execute(string args = "") + { + args = $"remove {_projectName} reference {args}"; + return ExecuteWithCapturedOutput(args); + } + + public RemoveReferenceCommand WithProject(string projectName) + { + _projectName = projectName; + return this; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RestoreCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RestoreCommand.cs new file mode 100644 index 000000000..d2b43b702 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RestoreCommand.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class RestoreCommand : DotnetCommand + { + private string _runtime; + + public RestoreCommand WithRuntime(string runtime) + { + _runtime = runtime; + + return this; + } + + public override CommandResult Execute(string args = "") + { + args = $"restore {GetRuntime()} {args} --disable-parallel"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"restore {GetRuntime()} {args} --disable-parallel"; + return base.ExecuteWithCapturedOutput(args); + } + + private string GetRuntime() + { + if (_runtime == null) + { + return null; + } + + return $"-property:RuntimeIdentifier={_runtime}"; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RunCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RunCommand.cs new file mode 100644 index 000000000..2cbfeabc8 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/RunCommand.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class RunCommand : DotnetCommand + { + public override CommandResult Execute(string args = "") + { + args = $"run {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"run {args}"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/StoreCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/StoreCommand.cs new file mode 100644 index 000000000..65e528761 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/StoreCommand.cs @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; +using NuGet.Frameworks; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class StoreCommand : DotnetCommand + { + private List _profileProject = new List(); + private string _framework; + private string _output; + private string _runtime; + private string _frameworkVersion; + private string _intermediateWorkingDirectory; + + public StoreCommand WithManifest(string profileProject) + { + _profileProject.Add($"--manifest {profileProject}"); + + return this; + } + public StoreCommand WithFramework(string framework) + { + _framework = framework; + return this; + } + + public StoreCommand WithFramework(NuGetFramework framework) => WithFramework(framework.GetShortFolderName()); + + public StoreCommand WithOutput(string output) + { + _output = output; + return this; + } + + public StoreCommand WithRuntime(string runtime) + { + _runtime = runtime; + return this; + } + + public StoreCommand WithRuntimeFrameworkVersion(string frameworkVersion) + { + _frameworkVersion = frameworkVersion; + return this; + } + + public StoreCommand WithIntermediateWorkingDirectory(string intermediateWorkingDirectory) + { + _intermediateWorkingDirectory = intermediateWorkingDirectory; + return this; + } + + public override CommandResult Execute(string args = "") + { + args = $"store {BuildArgs()} {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"store {BuildArgs()} {args}"; + return base.ExecuteWithCapturedOutput(args); + } + + private string BuildArgs() => string.Join(" ", + ProfileProjectOption, + FrameworkOption, + OutputOption, + IntermediateWorkingDirectoryOption, + RuntimeOption, + FrameworkVersionOption); + + private string ProfileProjectOption => string.Join(" ", _profileProject) ; + + private string FrameworkOption => string.IsNullOrEmpty(_framework) ? "" : $"-f {_framework}"; + + private string OutputOption => string.IsNullOrEmpty(_output) ? "" : $"-o {_output}"; + + private string RuntimeOption => string.IsNullOrEmpty(_runtime) ? "" : $"-r {_runtime}"; + + private string FrameworkVersionOption => string.IsNullOrEmpty(_frameworkVersion) ? "" : $" --framework-version {_frameworkVersion}"; + + private string IntermediateWorkingDirectoryOption => string.IsNullOrEmpty(_intermediateWorkingDirectory)? "" : $" -w {_intermediateWorkingDirectory}"; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs new file mode 100644 index 000000000..82ff520df --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs @@ -0,0 +1,256 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class TestCommand + { + private string _baseDirectory; + + private List _cliGeneratedEnvironmentVariables = new List { "MSBuildSDKsPath" }; + + protected string _command; + + public Process CurrentProcess { get; private set; } + + public Dictionary Environment { get; } = new Dictionary(); + + public event DataReceivedEventHandler ErrorDataReceived; + + public event DataReceivedEventHandler OutputDataReceived; + + public string WorkingDirectory { get; set; } + + public TestCommand(string command) + { + _command = command; + + _baseDirectory = GetBaseDirectory(); + } + + public void KillTree() + { + if (CurrentProcess == null) + { + throw new InvalidOperationException("No process is available to be killed"); + } + + CurrentProcess.KillTree(); + } + + public virtual CommandResult Execute(string args = "") => Task.Run(async () => await ExecuteAsync(args)).Result; + + public async virtual Task ExecuteAsync(string args = "") + { + var resolvedCommand = _command; + + ResolveCommand(ref resolvedCommand, ref args); + + Console.WriteLine($"Executing - {resolvedCommand} {args} - {WorkingDirectoryInfo()}"); + + return await ExecuteAsyncInternal(resolvedCommand, args); + } + + public virtual CommandResult ExecuteWithCapturedOutput(string args = "") + { + var resolvedCommand = _command; + + ResolveCommand(ref resolvedCommand, ref args); + + Console.WriteLine($"Executing (Captured Output) - {resolvedCommand} {args} - {WorkingDirectoryInfo()}"); + + return Task.Run(async () => await ExecuteAsyncInternal(resolvedCommand, args)).Result; + } + + private async Task ExecuteAsyncInternal(string executable, string args) + { + var stdOut = new List(); + + var stdErr = new List(); + + CurrentProcess = CreateProcess(executable, args); + + CurrentProcess.ErrorDataReceived += (s, e) => + { + stdErr.Add(e.Data); + + var handler = ErrorDataReceived; + + if (handler != null) + { + handler(s, e); + } + }; + + CurrentProcess.OutputDataReceived += (s, e) => + { + stdOut.Add(e.Data); + + var handler = OutputDataReceived; + + if (handler != null) + { + handler(s, e); + } + }; + + var completionTask = CurrentProcess.StartAndWaitForExitAsync(); + + CurrentProcess.BeginOutputReadLine(); + + CurrentProcess.BeginErrorReadLine(); + + await completionTask; + + CurrentProcess.WaitForExit(); + + RemoveNullTerminator(stdOut); + + RemoveNullTerminator(stdErr); + + return new CommandResult( + CurrentProcess.StartInfo, + CurrentProcess.ExitCode, + string.Join(System.Environment.NewLine, stdOut), + string.Join(System.Environment.NewLine, stdErr)); + } + + private Process CreateProcess(string executable, string args) + { + var psi = new ProcessStartInfo + { + FileName = executable, + Arguments = args, + RedirectStandardError = true, + RedirectStandardOutput = true, + RedirectStandardInput = true, + UseShellExecute = false + }; + + RemoveCliGeneratedEnvironmentVariablesFrom(psi); + + psi.Environment["DOTNET_MULTILEVEL_LOOKUP"] = "0"; + + // Set DOTNET_ROOT to ensure sub process find the same host fxr + string dotnetDirectoryPath = Path.GetDirectoryName(RepoDirectoriesProvider.DotnetUnderTest); + if (System.Environment.Is64BitProcess) + { + psi.Environment.Add("DOTNET_ROOT", dotnetDirectoryPath); + } + else + { + psi.Environment.Add("DOTNET_ROOT(x86)", dotnetDirectoryPath); + } + + AddEnvironmentVariablesTo(psi); + + AddWorkingDirectoryTo(psi); + + var process = new Process + { + StartInfo = psi + }; + + process.EnableRaisingEvents = true; + + return process; + } + + private string WorkingDirectoryInfo() + { + if (WorkingDirectory == null) + { + return ""; + } + + return $" in pwd {WorkingDirectory}"; + } + + private void RemoveNullTerminator(List strings) + { + var count = strings.Count; + + if (count < 1) + { + return; + } + + if (strings[count - 1] == null) + { + strings.RemoveAt(count - 1); + } + } + + private string GetBaseDirectory() => +#if NET451 + AppDomain.CurrentDomain.BaseDirectory; +#else + AppContext.BaseDirectory; +#endif + + private void ResolveCommand(ref string executable, ref string args) + { + if (executable.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + var newArgs = ArgumentEscaper.EscapeSingleArg(executable); + + if (!string.IsNullOrEmpty(args)) + { + newArgs += " " + args; + } + + args = newArgs; + + executable = RepoDirectoriesProvider.DotnetUnderTest; + } + else if ( executable == "dotnet") + { + executable = RepoDirectoriesProvider.DotnetUnderTest; + } + else if (!Path.IsPathRooted(executable)) + { + executable = Env.GetCommandPath(executable) ?? + Env.GetCommandPathFromRootPath(_baseDirectory, executable); + } + } + + private void RemoveCliGeneratedEnvironmentVariablesFrom(ProcessStartInfo psi) + { + foreach (var name in _cliGeneratedEnvironmentVariables) + { +#if NET451 + psi.EnvironmentVariables.Remove(name); +#else + psi.Environment.Remove(name); +#endif + } + } + + private void AddEnvironmentVariablesTo(ProcessStartInfo psi) + { + foreach (var item in Environment) + { +#if NET451 + psi.EnvironmentVariables[item.Key] = item.Value; +#else + psi.Environment[item.Key] = item.Value; +#endif + } + } + + private void AddWorkingDirectoryTo(ProcessStartInfo psi) + { + if (!string.IsNullOrWhiteSpace(WorkingDirectory)) + { + psi.WorkingDirectory = WorkingDirectory; + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ToolCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ToolCommand.cs new file mode 100644 index 000000000..d9099cd32 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/ToolCommand.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class ToolCommand : DotnetCommand + { + public override CommandResult Execute(string args = "") => base.Execute($"tool {args}"); + + public override CommandResult ExecuteWithCapturedOutput(string args = "") => base.ExecuteWithCapturedOutput($"tool {args}"); + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/VSTestCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/VSTestCommand.cs new file mode 100644 index 000000000..38c340ee1 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/VSTestCommand.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class VSTestCommand: DotnetCommand + { + public override CommandResult Execute(string args = "") + { + args = $"vstest {args}"; + return base.Execute(args); + } + + public override CommandResult ExecuteWithCapturedOutput(string args = "") + { + args = $"vstest {args}"; + return base.ExecuteWithCapturedOutput(args); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/DirectoryInfoExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/DirectoryInfoExtensions.cs new file mode 100644 index 000000000..7366a4153 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/DirectoryInfoExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; + +namespace Microsoft.DotNet.TestFramework +{ + internal static class DirectoryInfoExtensions + { + public static bool Contains(this DirectoryInfo subject, FileSystemInfo target) => target.FullName.StartsWith(subject.FullName); + + public static DirectoryInfo GetDirectory(this DirectoryInfo subject, params string[] directoryNames) => new DirectoryInfo(Path.Combine(subject.FullName, Path.Combine(directoryNames))); + + public static FileInfo GetFile(this DirectoryInfo subject, string fileName) => new FileInfo(Path.Combine(subject.FullName, fileName)); + + public static void EnsureExistsAndEmpty(this DirectoryInfo subject) + { + if (subject.Exists) + { + subject.Delete(true); + } + + subject.Create(); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/ProcessExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/ProcessExtensions.cs new file mode 100644 index 000000000..26287171a --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/ProcessExtensions.cs @@ -0,0 +1,133 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + internal static class ProcessExtensions + { +#if NET451 + private static readonly bool _isWindows = true; +#else + private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif + private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); + + public static void KillTree(this Process process) => process.KillTree(_defaultTimeout); + + public static void KillTree(this Process process, TimeSpan timeout) + { + string stdout; + if (_isWindows) + { + RunProcessAndWaitForExit( + "taskkill", + $"/T /F /PID {process.Id}", + timeout, + out stdout); + } + else + { + var children = new HashSet(); + GetAllChildIdsUnix(process.Id, children, timeout); + foreach (var childId in children) + { + KillProcessUnix(childId, timeout); + } + KillProcessUnix(process.Id, timeout); + } + } + + private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) + { + string stdout; + var exitCode = RunProcessAndWaitForExit( + "pgrep", + $"-P {parentId}", + timeout, + out stdout); + + if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) + { + using (var reader = new StringReader(stdout)) + { + while (true) + { + var text = reader.ReadLine(); + if (text == null) + { + return; + } + + int id; + if (int.TryParse(text, out id)) + { + children.Add(id); + // Recursively get the children + GetAllChildIdsUnix(id, children, timeout); + } + } + } + } + } + + private static void KillProcessUnix(int processId, TimeSpan timeout) + { + string stdout; + RunProcessAndWaitForExit( + "kill", + $"-TERM {processId}", + timeout, + out stdout); + } + + private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) + { + var startInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + + stdout = null; + if (process.WaitForExit((int)timeout.TotalMilliseconds)) + { + stdout = process.StandardOutput.ReadToEnd(); + } + else + { + process.Kill(); + } + + return process.ExitCode; + } + + public static Task StartAndWaitForExitAsync(this Process subject) + { + var taskCompletionSource = new TaskCompletionSource(); + + subject.EnableRaisingEvents = true; + + subject.Exited += (s, a) => + { + taskCompletionSource.SetResult(null); + + subject.Dispose(); + }; + + subject.Start(); + + return taskCompletionSource.Task; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/TestCommandExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/TestCommandExtensions.cs new file mode 100644 index 000000000..f8254f3a7 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/TestCommandExtensions.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public static class TestCommandExtensions + { + public static TCommand WithWorkingDirectory(this TCommand subject, string workingDirectory) where TCommand : TestCommand + { + subject.WorkingDirectory = workingDirectory; + + return subject; + } + + public static TCommand WithWorkingDirectory(this TCommand subject, DirectoryInfo workingDirectory) where TCommand : TestCommand + { + subject.WorkingDirectory = workingDirectory.FullName; + + return subject; + } + + public static TCommand WithEnvironmentVariable(this TCommand subject, string name, string value) where TCommand : TestCommand + { + subject.Environment.Add(name, value); + + return subject; + } + + public static TCommand WithOutputDataReceivedHandler(this TCommand subject, Action writeLine) where TCommand : TestCommand + { + subject.OutputDataReceived += (s, e) => writeLine(e.Data); + + return subject; + } + + public static TCommand WithErrorDataReceivedHandler(this TCommand subject, Action writeLine) where TCommand : TestCommand + { + subject.ErrorDataReceived += (s, e) => writeLine(e.Data); + + return subject; + } + + public static TCommand WithForwardingToConsole(this TCommand subject) where TCommand : TestCommand + { + subject.WithOutputDataReceivedHandler(s => Console.Out.WriteLine(s)); + + subject.WithErrorDataReceivedHandler(s => Console.Error.WriteLine(s)); + + return subject; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Microsoft.DotNet.Tools.Tests.Utilities.csproj b/test/Microsoft.DotNet.Tools.Tests.Utilities/Microsoft.DotNet.Tools.Tests.Utilities.csproj new file mode 100644 index 000000000..989ab06c9 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Microsoft.DotNet.Tools.Tests.Utilities.csproj @@ -0,0 +1,13 @@ + + + $(CoreSdkTargetFramework) + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/NuGetFrameworks.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/NuGetFrameworks.cs new file mode 100644 index 000000000..bdbafc815 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/NuGetFrameworks.cs @@ -0,0 +1,15 @@ +using NuGet.Frameworks; +using System; + +namespace Microsoft.DotNet.Tools.Tests.Utilities +{ + // This class is for frameworks that aren't yet in NuGet's FrameworkConstants.CommonFrameworks class + public static class NuGetFrameworks + { + public static readonly NuGetFramework NetCoreApp21 + = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, new Version(2, 1, 0, 0)); + + public static readonly NuGetFramework NetCoreApp22 + = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, new Version(2, 2, 0, 0)); + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/PlatformSpecificFact.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/PlatformSpecificFact.cs new file mode 100644 index 000000000..819cd0644 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/PlatformSpecificFact.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class PlatformSpecificFact : FactAttribute + { + public PlatformSpecificFact(TestPlatforms platforms) + { + if (ShouldSkip(platforms)) + { + this.Skip = "This test is not supported on this platform."; + } + } + + internal static bool ShouldSkip(TestPlatforms platforms) => + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !platforms.HasFlag(TestPlatforms.Windows)) + || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && !platforms.HasFlag(TestPlatforms.Linux)) + || (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && !platforms.HasFlag(TestPlatforms.OSX)) + || (RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")) && !platforms.HasFlag(TestPlatforms.FreeBSD)); + } + + [Flags] + public enum TestPlatforms + { + Any = -1, + Windows = 1, + Linux = 2, + OSX = 4, + FreeBSD = 8, + NetBSD = 16, + illumos = 32, + Solaris = 64, + iOS = 128, + tvOS = 256, + Android = 512, + Browser = 1024, + AnyUnix = 2048 + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/PlatformSpecificTheory.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/PlatformSpecificTheory.cs new file mode 100644 index 000000000..a23581dff --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/PlatformSpecificTheory.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class PlatformSpecificTheory : TheoryAttribute + { + public PlatformSpecificTheory(TestPlatforms platforms) + { + if (PlatformSpecificFact.ShouldSkip(platforms)) + { + this.Skip = "This test is not supported on this platform."; + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Properties/Properties.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Properties/Properties.cs new file mode 100644 index 000000000..be0d4944b --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Properties/Properties.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Extensions.DependencyModel.Tests , PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.DotNet.Configurer.UnitTests , PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("dotnet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.DotNet.ToolPackage.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.DotNet.ShellShim.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/RepoDirectoriesProvider.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/RepoDirectoriesProvider.cs new file mode 100644 index 000000000..4546a34c6 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/RepoDirectoriesProvider.cs @@ -0,0 +1,82 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using FluentAssertions; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class RepoDirectoriesProvider + { + public readonly static string RepoRoot; + public readonly static string TestWorkingFolder; + public readonly static string DotnetUnderTest; + public readonly static string DotnetRidUnderTest; + public readonly static string Stage2AspNetCore; + + static RepoDirectoriesProvider() + { + +#if NET451 + string directory = AppDomain.CurrentDomain.BaseDirectory; +#else + string directory = AppContext.BaseDirectory; +#endif + + while (directory != null) + { + var gitDirOrFile = Path.Combine(directory, ".git"); + if (Directory.Exists(gitDirOrFile) || File.Exists(gitDirOrFile)) + { + break; + } + directory = Directory.GetParent(directory)?.FullName; + } + + RepoRoot = directory; + + TestWorkingFolder = Environment.GetEnvironmentVariable("CORESDK_TEST_FOLDER"); + if (string.IsNullOrEmpty(TestWorkingFolder)) + { + TestWorkingFolder = Path.Combine(AppContext.BaseDirectory, "Tests"); + } + + DotnetUnderTest = Environment.GetEnvironmentVariable("DOTNET_UNDER_TEST"); + string dotnetExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : ""; + if (string.IsNullOrEmpty(DotnetUnderTest)) + { + if (RepoRoot == null) + { + DotnetUnderTest = "dotnet" + dotnetExtension; + } + else + { + // https://stackoverflow.com/a/60545278/294804 + var assemblyConfigurationAttribute = typeof(RepoDirectoriesProvider).Assembly.GetCustomAttribute(); + string configuration = assemblyConfigurationAttribute?.Configuration; + DotnetUnderTest = Path.Combine(RepoRoot, "artifacts", "bin", "redist", configuration, "dotnet", "dotnet" + dotnetExtension); + } + } + + string AspNetCoreDir = Path.Combine(Path.GetDirectoryName(DotnetUnderTest), "shared", "Microsoft.AspNetCore.App"); + if (Directory.Exists(AspNetCoreDir)) + { + Stage2AspNetCore = Directory.EnumerateDirectories(AspNetCoreDir).First(); + } + + // TODO: Resolve dotnet folder even if DotnetUnderTest doesn't have full path + var sdkFolders = Directory.GetDirectories(Path.Combine(Path.GetDirectoryName(DotnetUnderTest), "sdk")); + sdkFolders.Length.Should().Be(1, "Only one SDK folder is expected in the layout"); + + var sdkFolder = sdkFolders.Single(); + var versionFile = Path.Combine(sdkFolder, ".version"); + + var lines = File.ReadAllLines(versionFile); + DotnetRidUnderTest = lines[2].Trim(); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/RequiresAspNetCore.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/RequiresAspNetCore.cs new file mode 100644 index 000000000..e7df9bf8a --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/RequiresAspNetCore.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class RequiresAspNetCore : FactAttribute + { + public RequiresAspNetCore() + { + if (RepoDirectoriesProvider.Stage2AspNetCore == null) + { + this.Skip = $"This test requires a AspNetCore but it isn't present."; + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/RuntimeConfig.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/RuntimeConfig.cs new file mode 100644 index 000000000..1bc704348 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/RuntimeConfig.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Text.Json; +using System.IO; + +namespace Microsoft.DotNet.TestFramework +{ + internal class RuntimeConfigFramework + { + public string Name { get; set; } + public string Version { get; set; } + } + + public class RuntimeConfig + { + public bool IsPortable { get; } + internal RuntimeConfigFramework Framework { get; } + + public RuntimeConfig(string runtimeConfigPath) + { + var jsonDocumentOptions = new JsonDocumentOptions + { + AllowTrailingCommas = true, + CommentHandling = JsonCommentHandling.Skip + }; + + using (var stream = File.OpenRead(runtimeConfigPath)) + using (JsonDocument doc = JsonDocument.Parse(stream, jsonDocumentOptions)) + { + JsonElement root = doc.RootElement; + if (root.TryGetProperty("runtimeOptions", out var runtimeOptionsRoot)) + { + if (runtimeOptionsRoot.TryGetProperty("framework", out var framework)) + { + var runtimeConfigFramework = new RuntimeConfigFramework(); + string name = null; + string version = null; + foreach (var property in framework.EnumerateObject()) + { + if (property.Name.Equals("name", StringComparison.OrdinalIgnoreCase)) + { + name = property.Value.GetString(); + } + + if (property.Name.Equals("version", StringComparison.OrdinalIgnoreCase)) + { + version = property.Value.GetString(); + } + } + + if (name == null || version == null) + { + Framework = null; + } + else + { + Framework = new RuntimeConfigFramework + { + Name = name, + Version = version + }; + } + } + else + { + Framework = null; + } + } + } + + IsPortable = Framework != null; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/DisposableDirectory.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/DisposableDirectory.cs new file mode 100644 index 000000000..8baa5a15e --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/DisposableDirectory.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class DisposableDirectory : TempDirectory, IDisposable + { + public DisposableDirectory(TempRoot root) + : base(root) + { + } + + public void Dispose() + { + if (Path != null && Directory.Exists(Path)) + { + try + { + Directory.Delete(Path, recursive: true); + } + catch + { + } + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/DisposableFile.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/DisposableFile.cs new file mode 100644 index 000000000..c17c1dd9b --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/DisposableFile.cs @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class DisposableFile : TempFile, IDisposable + { + public DisposableFile(string path) + : base(path) + { + } + + public DisposableFile(string prefix = null, string extension = null, string directory = null, string callerSourcePath = null, int callerLineNumber = 0) + : base(prefix, extension, directory, callerSourcePath, callerLineNumber) + { + } + + public void Dispose() + { + if (Path != null && File.Exists(Path)) + { + try + { + File.Delete(Path); + } + catch (UnauthorizedAccessException) + { + try + { + // the file might still be memory-mapped, delete on close: + DeleteFileOnClose(Path); + } + catch (IOException ex) + { + throw new InvalidOperationException(string.Format(@" +The file '{0}' seems to have been opened in a way that prevents us from deleting it on close. +Is the file loaded as an assembly (e.g. via Assembly.LoadFile)? + +{1}: {2}", Path, ex.GetType().Name, ex.Message), ex); + } + catch (UnauthorizedAccessException) + { + // We should ignore this exception if we got it the second time, + // the most important reason is that the file has already been + // scheduled for deletion and will be deleted when all handles + // are closed. + } + } + } + } + + [DllImport("kernel32.dll", PreserveSig = false)] + private static extern void SetFileInformationByHandle(SafeFileHandle handle, int fileInformationClass, ref uint fileDispositionInfoDeleteFile, int bufferSize); + + private const int FileDispositionInfo = 4; + + internal static void PrepareDeleteOnCloseStreamForDisposal(FileStream stream) + { + // tomat: Set disposition to "delete" on the stream, so to avoid ForeFront EndPoint + // Protection driver scanning the file. Note that after calling this on a file that's open with DeleteOnClose, + // the file can't be opened again, not even by the same process. + uint trueValue = 1; + SetFileInformationByHandle(stream.SafeFileHandle, FileDispositionInfo, ref trueValue, sizeof(uint)); + } + + /// + /// Marks given file for automatic deletion when all its handles are closed. + /// Note that after doing this the file can't be opened again, not even by the same process. + /// + internal static void DeleteFileOnClose(string fullPath) + { + using (var stream = new FileStream(fullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.Delete | FileShare.ReadWrite, 8, FileOptions.DeleteOnClose)) + { + PrepareDeleteOnCloseStreamForDisposal(stream); + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/FileNameUtilities.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/FileNameUtilities.cs new file mode 100644 index 000000000..73c34e641 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/FileNameUtilities.cs @@ -0,0 +1,179 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + /// + /// Implements a few file name utilities that are needed by the compiler. + /// In general the compiler is not supposed to understand the format of the paths. + /// In rare cases it needs to check if a string is a valid file name or change the extension + /// (embedded resources, netmodules, output name). + /// The APIs are intentionally limited to cover just these rare cases. Do not add more APIs. + /// + internal static class FileNameUtilities + { + internal const char DirectorySeparatorChar = '\\'; + internal const char AltDirectorySeparatorChar = '/'; + internal const char VolumeSeparatorChar = ':'; + + /// + /// Returns true if the string represents an unqualified file name. + /// The name may contain any characters but directory and volume separators. + /// + /// Path. + /// + /// True if is a simple file name, false if it is null or includes a directory specification. + /// + internal static bool IsFileName(string path) => IndexOfFileName(path) == 0; + + /// + /// Returns the offset in where the dot that starts an extension is, or -1 if the path doesn't have an extension. + /// + /// + /// Returns 0 for path ".foo". + /// Returns -1 for path "foo.". + /// + private static int IndexOfExtension(string path) + { + if (path == null) + { + return -1; + } + + int length = path.Length; + int i = length; + + while (--i >= 0) + { + char c = path[i]; + if (c == '.') + { + if (i != length - 1) + { + return i; + } + + return -1; + } + + if (c == DirectorySeparatorChar || c == AltDirectorySeparatorChar || c == VolumeSeparatorChar) + { + break; + } + } + + return -1; + } + + /// + /// Returns an extension of the specified path string. + /// + /// + /// The same functionality as but doesn't throw an exception + /// if there are invalid characters in the path. + /// + internal static string GetExtension(string path) + { + if (path == null) + { + return null; + } + + int index = IndexOfExtension(path); + return (index >= 0) ? path.Substring(index) : string.Empty; + } + + /// + /// Removes extension from path. + /// + /// + /// Returns "foo" for path "foo.". + /// Returns "foo.." for path "foo...". + /// + private static string RemoveExtension(string path) + { + if (path == null) + { + return null; + } + + int index = IndexOfExtension(path); + if (index >= 0) + { + return path.Substring(0, index); + } + + // trim last ".", if present + if (path.Length > 0 && path[path.Length - 1] == '.') + { + return path.Substring(0, path.Length - 1); + } + + return path; + } + + /// + /// Returns path with the extension changed to . + /// + /// + /// Equivalent of + /// + /// If is null, returns null. + /// If path does not end with an extension, the new extension is appended to the path. + /// If extension is null, equivalent to . + /// + internal static string ChangeExtension(string path, string extension) + { + if (path == null) + { + return null; + } + + var pathWithoutExtension = RemoveExtension(path); + if (extension == null || path.Length == 0) + { + return pathWithoutExtension; + } + + if (extension.Length == 0 || extension[0] != '.') + { + return pathWithoutExtension + "." + extension; + } + + return pathWithoutExtension + extension; + } + + /// + /// Returns the position in given path where the file name starts. + /// + /// -1 if path is null. + internal static int IndexOfFileName(string path) + { + if (path == null) + { + return -1; + } + + for (int i = path.Length - 1; i >= 0; i--) + { + char ch = path[i]; + if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar) + { + return i + 1; + } + } + + return 0; + } + + /// + /// Get file name from path. + /// + /// Unlike doesn't check for invalid path characters. + internal static string GetFileName(string path) + { + int fileNameStart = IndexOfFileName(path); + return (fileNameStart <= 0) ? path : path.Substring(fileNameStart); + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/ImmutableArrayTestExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/ImmutableArrayTestExtensions.cs new file mode 100644 index 000000000..c9c1eb591 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/ImmutableArrayTestExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + /// + /// The collection of extension methods for the type + /// + public static class ImmutableArrayTestExtensions + { + /// + /// Writes read-only array of bytes to the specified file. + /// + /// Data to write to the file. + /// File path. + internal static void WriteToFile(this ImmutableArray bytes, string path) + { + Debug.Assert(!bytes.IsDefault); + + const int bufferSize = 4096; + using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize)) + { + // PERF: Consider using an ObjectPool here + byte[] buffer = new byte[Math.Min(bufferSize, bytes.Length)]; + + int offset = 0; + while (offset < bytes.Length) + { + int length = Math.Min(bufferSize, bytes.Length - offset); + bytes.CopyTo(offset, buffer, 0, length); + fileStream.Write(buffer, 0, length); + offset += length; + } + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/PathKind.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/PathKind.cs new file mode 100644 index 000000000..349ee5b3e --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/PathKind.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + internal enum PathKind + { + /// + /// Null or empty. + /// + Empty, + + /// + /// "file" + /// + Relative, + + /// + /// ".\file" + /// + RelativeToCurrentDirectory, + + /// + /// "..\file" + /// + RelativeToCurrentParent, + + /// + /// "\dir\file" + /// + RelativeToCurrentRoot, + + /// + /// "C:dir\file" + /// + RelativeToDriveDirectory, + + /// + /// "C:\file" or "\\machine" (UNC). + /// + Absolute, + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/PathUtilities.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/PathUtilities.cs new file mode 100644 index 000000000..e27f30c8e --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/PathUtilities.cs @@ -0,0 +1,358 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + // Contains path parsing utilities. + // We need our own because System.IO.Path is insufficient for our purposes + // For example we need to be able to work with invalid paths or paths containing wildcards + internal static class PathUtilities + { + // We consider '/' a directory separator on Unix like systems. + // On Windows both / and \ are equally accepted. + internal static readonly char DirectorySeparatorChar = IsUnixLikePlatform ? '/' : '\\'; + internal static readonly char AltDirectorySeparatorChar = '/'; + internal static readonly string DirectorySeparatorStr = new string(DirectorySeparatorChar, 1); + internal const char VolumeSeparatorChar = ':'; + + private static bool IsUnixLikePlatform => Path.DirectorySeparatorChar == '/'; + + internal static bool IsDirectorySeparator(char c) => c == DirectorySeparatorChar || c == AltDirectorySeparatorChar; + + internal static string TrimTrailingSeparators(string s) + { + int lastSeparator = s.Length; + while (lastSeparator > 0 && IsDirectorySeparator(s[lastSeparator - 1])) + { + lastSeparator = lastSeparator - 1; + } + + if (lastSeparator != s.Length) + { + s = s.Substring(0, lastSeparator); + } + + return s; + } + + internal static string GetExtension(string path) => FileNameUtilities.GetExtension(path); + + internal static string ChangeExtension(string path, string extension) => FileNameUtilities.ChangeExtension(path, extension); + + internal static string RemoveExtension(string path) => FileNameUtilities.ChangeExtension(path, extension: null); + + internal static string GetFileName(string path) => FileNameUtilities.GetFileName(path); + + /// + /// Get directory name from path. + /// + /// + /// Unlike it + /// doesn't check for invalid path characters, + /// doesn't strip any trailing directory separators (TODO: tomat), + /// doesn't recognize UNC structure \\computer-name\share\directory-name\file-name (TODO: tomat). + /// + /// Prefix of path that represents a directory. + internal static string GetDirectoryName(string path) + { + int fileNameStart = FileNameUtilities.IndexOfFileName(path); + if (fileNameStart < 0) + { + return null; + } + + return path.Substring(0, fileNameStart); + } + + internal static PathKind GetPathKind(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + return PathKind.Empty; + } + + // "C:\" + // "\\machine" (UNC) + // "/etc" (Unix) + if (IsAbsolute(path)) + { + return PathKind.Absolute; + } + + // "." + // ".." + // ".\" + // "..\" + if (path.Length > 0 && path[0] == '.') + { + if (path.Length == 1 || IsDirectorySeparator(path[1])) + { + return PathKind.RelativeToCurrentDirectory; + } + + if (path[1] == '.') + { + if (path.Length == 2 || IsDirectorySeparator(path[2])) + { + return PathKind.RelativeToCurrentParent; + } + } + } + + if (!IsUnixLikePlatform) + { + // "\" + // "\foo" + if (path.Length >= 1 && IsDirectorySeparator(path[0])) + { + return PathKind.RelativeToCurrentRoot; + } + + // "C:foo" + + if (path.Length >= 2 && path[1] == VolumeSeparatorChar && (path.Length <= 2 || !IsDirectorySeparator(path[2]))) + { + return PathKind.RelativeToDriveDirectory; + } + } + + // "foo.dll" + return PathKind.Relative; + } + + internal static bool IsAbsolute(string path) + { + if (string.IsNullOrEmpty(path)) + { + return false; + } + + if (IsUnixLikePlatform) + { + return path[0] == DirectorySeparatorChar; + } + + // "C:\" + if (IsDriveRootedAbsolutePath(path)) + { + // Including invalid paths (e.g. "*:\") + return true; + } + + // "\\machine\share" + // Including invalid/incomplete UNC paths (e.g. "\\foo") + return path.Length >= 2 && + IsDirectorySeparator(path[0]) && + IsDirectorySeparator(path[1]); + } + + /// + /// Returns true if given path is absolute and starts with a drive specification ("C:\"). + /// + private static bool IsDriveRootedAbsolutePath(string path) + { + Debug.Assert(!IsUnixLikePlatform); + return path.Length >= 3 && path[1] == VolumeSeparatorChar && IsDirectorySeparator(path[2]); + } + + /// + /// Get a prefix of given path which is the root of the path. + /// + /// + /// Root of an absolute path or null if the path isn't absolute or has invalid format (e.g. "\\"). + /// It may or may not end with a directory separator (e.g. "C:\", "C:\foo", "\\machine\share", etc.) . + /// + internal static string GetPathRoot(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + return null; + } + + int length = GetPathRootLength(path); + return (length != -1) ? path.Substring(0, length) : null; + } + + private static int GetPathRootLength(string path) + { + Debug.Assert(!string.IsNullOrEmpty(path)); + + if (IsUnixLikePlatform) + { + if (IsDirectorySeparator(path[0])) + { + // "/*" + return 1; + } + } + else + { + // "C:\" + if (IsDriveRootedAbsolutePath(path)) + { + return 3; + } + + if (IsDirectorySeparator(path[0])) + { + // "\\machine\share" + return GetUncPathRootLength(path); + } + } + + return -1; + } + + /// + /// Calculates the length of root of an UNC path. + /// + /// + /// "\\server\share" is root of UNC path "\\server\share\dir1\dir2\file". + /// + private static int GetUncPathRootLength(string path) + { + Debug.Assert(IsDirectorySeparator(path[0])); + + // root: + // [directory-separator]{2,}[^directory-separator]+[directory-separator]+[^directory-separator]+ + + int serverIndex = IndexOfNonDirectorySeparator(path, 1); + if (serverIndex < 2) + { + return -1; + } + + int separator = IndexOfDirectorySeparator(path, serverIndex); + if (separator == -1) + { + return -1; + } + + int shareIndex = IndexOfNonDirectorySeparator(path, separator); + if (shareIndex == -1) + { + return -1; + } + + int rootEnd = IndexOfDirectorySeparator(path, shareIndex); + return rootEnd == -1 ? path.Length : rootEnd; + } + + private static int IndexOfDirectorySeparator(string path, int start) + { + for (int i = start; i < path.Length; i++) + { + if (IsDirectorySeparator(path[i])) + { + return i; + } + } + + return -1; + } + + private static int IndexOfNonDirectorySeparator(string path, int start) + { + for (int i = start; i < path.Length; i++) + { + if (!IsDirectorySeparator(path[i])) + { + return i; + } + } + + return -1; + } + + /// + /// Combines an absolute path with a relative. + /// + /// Absolute root path. + /// Relative path. + /// + /// An absolute combined path, or null if is + /// absolute (e.g. "C:\abc", "\\machine\share\abc"), + /// relative to the current root (e.g. "\abc"), + /// or relative to a drive directory (e.g. "C:abc\def"). + /// + /// + internal static string CombineAbsoluteAndRelativePaths(string root, string relativePath) + { + Debug.Assert(IsAbsolute(root)); + + return CombinePossiblyRelativeAndRelativePaths(root, relativePath); + } + + /// + /// Combine two paths, the first of which may be absolute. + /// + /// First path: absolute, relative, or null. + /// Second path: relative and non-null. + /// null, if is null; a combined path, otherwise. + /// + internal static string CombinePossiblyRelativeAndRelativePaths(string rootOpt, string relativePath) + { + if (string.IsNullOrEmpty(rootOpt)) + { + return null; + } + + switch (GetPathKind(relativePath)) + { + case PathKind.Empty: + return rootOpt; + + case PathKind.Absolute: + case PathKind.RelativeToCurrentRoot: + case PathKind.RelativeToDriveDirectory: + return null; + } + + return CombinePathsUnchecked(rootOpt, relativePath); + } + + internal static string CombinePathsUnchecked(string root, string relativePath) + { + Debug.Assert(!string.IsNullOrEmpty(root)); + + char c = root[root.Length - 1]; + if (!IsDirectorySeparator(c) && c != VolumeSeparatorChar) + { + return root + DirectorySeparatorStr + relativePath; + } + + return root + relativePath; + } + + internal static string RemoveTrailingDirectorySeparator(string path) + { + if (path.Length > 0 && IsDirectorySeparator(path[path.Length - 1])) + { + return path.Substring(0, path.Length - 1); + } + else + { + return path; + } + } + + /// + /// Determines whether an assembly reference is considered an assembly file path or an assembly name. + /// used, for example, on values of /r and #r. + /// + internal static bool IsFilePath(string assemblyDisplayNameOrPath) + { + Debug.Assert(assemblyDisplayNameOrPath != null); + + string extension = FileNameUtilities.GetExtension(assemblyDisplayNameOrPath); + return string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase) + || string.Equals(extension, ".exe", StringComparison.OrdinalIgnoreCase) + || assemblyDisplayNameOrPath.IndexOf(DirectorySeparatorChar) != -1 + || assemblyDisplayNameOrPath.IndexOf(AltDirectorySeparatorChar) != -1; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempDirectory.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempDirectory.cs new file mode 100644 index 000000000..46c048163 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempDirectory.cs @@ -0,0 +1,112 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.IO; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class TempDirectory + { + private readonly string _path; + private readonly TempRoot _root; + + protected TempDirectory(TempRoot root) + : this(CreateUniqueDirectory(TempRoot.Root), root) + { + } + + private TempDirectory(string path, TempRoot root) + { + Debug.Assert(path != null); + Debug.Assert(root != null); + + _path = path; + _root = root; + } + + private static string CreateUniqueDirectory(string basePath) + { + while (true) + { + string dir = System.IO.Path.Combine(basePath, Guid.NewGuid().ToString()); + try + { + Directory.CreateDirectory(dir); + return dir; + } + catch (IOException) + { + // retry + } + } + } + + public string Path => _path; + + public DirectoryInfo DirectoryInfo => new DirectoryInfo(Path); + + /// + /// Creates a file in this directory. + /// + /// File name. + public TempFile CreateFile(string name) + { + string filePath = System.IO.Path.Combine(_path, name); + TempRoot.CreateStream(filePath); + return _root.AddFile(new DisposableFile(filePath)); + } + + /// + /// Creates a file in this directory that is a copy of the specified file. + /// + public TempFile CopyFile(string originalPath) + { + string name = System.IO.Path.GetFileName(originalPath); + string filePath = System.IO.Path.Combine(_path, name); + File.Copy(originalPath, filePath); + return _root.AddFile(new DisposableFile(filePath)); + } + + /// + /// Recursively copy the provided directory into this TempDirectory. + /// Does not handle links. + /// + /// + /// + public TempDirectory CopyDirectory(string sourceDirectory) + { + Debug.Assert(Directory.Exists(sourceDirectory), $"{sourceDirectory} does not exists"); + + var tempCopy = CreateDirectory(new DirectoryInfo(sourceDirectory).Name); + + foreach(var file in Directory.EnumerateFiles(sourceDirectory)) + { + tempCopy.CopyFile(file); + } + + foreach(var directory in Directory.EnumerateDirectories(sourceDirectory)) + { + tempCopy.CopyDirectory(directory); + } + + return tempCopy; + } + + /// + /// Creates a subdirectory in this directory. + /// + /// Directory name or unrooted directory path. + public TempDirectory CreateDirectory(string name) + { + string dirPath = System.IO.Path.Combine(_path, name); + Directory.CreateDirectory(dirPath); + return new TempDirectory(dirPath, _root); + } + + public void SetCurrentDirectory() => Directory.SetCurrentDirectory(_path); + + public override string ToString() => _path; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempFile.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempFile.cs new file mode 100644 index 000000000..6b328e7d4 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempFile.cs @@ -0,0 +1,100 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Immutable; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class TempFile + { + private readonly string _path; + + internal TempFile(string path) + { + Debug.Assert(PathUtilities.IsAbsolute(path)); + _path = path; + } + + internal TempFile(string prefix, string extension, string directory, string callerSourcePath, int callerLineNumber) + { + while (true) + { + if (prefix == null) + { + prefix = System.IO.Path.GetFileName(callerSourcePath) + "_" + callerLineNumber.ToString() + "_"; + } + + _path = System.IO.Path.Combine(directory ?? TempRoot.Root, prefix + Guid.NewGuid() + (extension ?? ".tmp")); + + try + { + TempRoot.CreateStream(_path); + break; + } + catch (PathTooLongException) + { + throw; + } + catch (DirectoryNotFoundException) + { + throw; + } + catch (IOException) + { + // retry + } + } + } + + public FileStream Open(FileAccess access = FileAccess.ReadWrite) => new FileStream(_path, FileMode.Open, access); + + public string Path => _path; + + public TempFile WriteAllText(string content, Encoding encoding) + { + File.WriteAllText(_path, content, encoding); + return this; + } + + public TempFile WriteAllText(string content) + { + File.WriteAllText(_path, content); + return this; + } + + public async Task WriteAllTextAsync(string content, Encoding encoding) + { + using (var sw = new StreamWriter(File.Create(_path), encoding)) + { + await sw.WriteAsync(content).ConfigureAwait(false); + } + + return this; + } + + public Task WriteAllTextAsync(string content) => WriteAllTextAsync(content, Encoding.UTF8); + + public TempFile WriteAllBytes(byte[] content) + { + File.WriteAllBytes(_path, content); + return this; + } + + public TempFile WriteAllBytes(ImmutableArray content) + { + content.WriteToFile(_path); + return this; + } + + public string ReadAllText() => File.ReadAllText(_path); + + public TempFile CopyContentFrom(string path) => WriteAllBytes(File.ReadAllBytes(path)); + + public override string ToString() => _path; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempRoot.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempRoot.cs new file mode 100644 index 000000000..f74611188 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TempFileSystem/TempRoot.cs @@ -0,0 +1,78 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class TempRoot : IDisposable + { + private static readonly bool DoDispose; + private readonly List _temps = new List(); + public static readonly string Root; + + static TempRoot() + { + var persistedRoot = Environment.GetEnvironmentVariable("TEST_ARTIFACTS"); + + if (string.IsNullOrWhiteSpace(persistedRoot)) + { + Root = Path.Combine(Path.GetTempPath(), "DotnetCLITests"); + DoDispose = true; + } + else + { + Root = persistedRoot; + DoDispose = false; + } + + Directory.CreateDirectory(Root); + } + + public void Dispose() + { + if (!DoDispose || _temps == null) return; + + DisposeAll(_temps); + _temps.Clear(); + } + + private static void DisposeAll(IEnumerable temps) + { + foreach (var temp in temps) + { + try + { + temp?.Dispose(); + } + catch + { + // ignore + } + } + } + + public DisposableDirectory CreateDirectory() + { + var dir = new DisposableDirectory(this); + _temps.Add(dir); + return dir; + } + + public TempFile CreateFile(string prefix = null, string extension = null, string directory = null, [CallerFilePath] string callerSourcePath = null, [CallerLineNumber] int callerLineNumber = 0) => AddFile(new DisposableFile(prefix, extension, directory, callerSourcePath, callerLineNumber)); + + public DisposableFile AddFile(DisposableFile file) + { + _temps.Add(file); + return file; + } + + internal static void CreateStream(string fullPath) + { + using (var file = new FileStream(fullPath, FileMode.CreateNew)) { } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInfo.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInfo.cs new file mode 100644 index 000000000..6de2d1442 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInfo.cs @@ -0,0 +1,81 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Microsoft.DotNet.TestFramework +{ + public class TestAssetInfo + { + // This is needed each release after we upgrade to 9.0 but the templates haven't been upgraded yet + + public static readonly string currentTfm = "net9.0"; + + private readonly string [] FilesToExclude = { ".DS_Store", ".noautobuild" }; + + public string AssetName { get; private set; } + + public FileInfo DotnetExeFile => _testAssets.DotnetCsprojExe; + + public string ProjectFilePattern => "*.csproj"; + + public DirectoryInfo Root { get; private set; } + + private TestAssets _testAssets { get; } + + internal TestAssetInfo(DirectoryInfo root, string assetName, TestAssets testAssets) + { + if (root == null) + { + throw new ArgumentNullException(nameof(root)); + } + + if (string.IsNullOrWhiteSpace(assetName)) + { + throw new ArgumentException("Argument cannot be null or whitespace", nameof(assetName)); + } + + if (testAssets == null) + { + throw new ArgumentNullException(nameof(testAssets)); + } + + Root = root; + + AssetName = assetName; + + _testAssets = testAssets; + } + + public TestAssetInstance CreateInstance([CallerMemberName] string callingMethod = "", string identifier = "") + { + var instancePath = GetTestDestinationDirectory(callingMethod, identifier); + + var testInstance = new TestAssetInstance(this, instancePath); + + return testInstance; + } + + internal IEnumerable GetSourceFiles() + { + ThrowIfTestAssetDoesNotExist(); + + return Root.GetFiles("*.*", SearchOption.AllDirectories) + .Where(f => !FilesToExclude.Contains(f.Name)); + } + + private DirectoryInfo GetTestDestinationDirectory(string callingMethod, string identifier) => _testAssets.CreateTestDirectory(AssetName, callingMethod, identifier); + + private void ThrowIfTestAssetDoesNotExist() + { + if (!Root.Exists) + { + throw new DirectoryNotFoundException($"Directory not found at '{Root.FullName}'"); + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInstance.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInstance.cs new file mode 100644 index 000000000..087fb1682 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInstance.cs @@ -0,0 +1,229 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Xml.Linq; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Tools.Test.Utilities; + +namespace Microsoft.DotNet.TestFramework +{ + public class TestAssetInstance + { + public DirectoryInfo MigrationBackupRoot { get; } + + public DirectoryInfo Root { get; } + + public TestAssetInfo TestAssetInfo { get; } + + private bool _filesCopied = false; + + private bool _restored = false; + + public TestAssetInstance(TestAssetInfo testAssetInfo, DirectoryInfo root) + { + if (testAssetInfo == null) + { + throw new ArgumentException(nameof(testAssetInfo)); + } + + if (root == null) + { + throw new ArgumentException(nameof(root)); + } + + TestAssetInfo = testAssetInfo; + + Root = root; + + MigrationBackupRoot = new DirectoryInfo(Path.Combine(root.Parent.FullName, "backup")); + + if (Root.Exists) + { + try + { + Root.Delete(recursive: true); + } + catch (IOException ex) + { + throw new InvalidOperationException("Unable to delete directory: " + Root.FullName, ex); + } + } + + Root.Create(); + + if (MigrationBackupRoot.Exists) + { + MigrationBackupRoot.Delete(recursive: true); + } + } + + public TestAssetInstance WithSourceFiles() + { + if (!_filesCopied) + { + CopySourceFiles(); + + _filesCopied = true; + } + + return this; + } + + public TestAssetInstance WithRestoreFiles() + { + if (!_restored) + { + WithSourceFiles(); + + RestoreAllProjects(); + + _restored = true; + } + + return this; + } + + public TestAssetInstance WithNuGetConfig(string nugetCache, string externalRestoreSources = null) + { + var thisAssembly = typeof(TestAssetInstance).GetTypeInfo().Assembly; + var newNuGetConfig = Root.GetFile("NuGet.Config"); + externalRestoreSources = externalRestoreSources ?? string.Empty; + + var content = @" + + + + $externalRestoreSources$ + + "; + content = content + .Replace("$fullpath$", nugetCache) + .Replace("$externalRestoreSources$", externalRestoreSources); + + using (var newNuGetConfigStream = + new FileStream(newNuGetConfig.FullName, FileMode.Create, FileAccess.Write)) + { + var contentBytes = new UTF8Encoding(true).GetBytes(content); + newNuGetConfigStream.Write(contentBytes, 0, contentBytes.Length); + } + + return this; + } + + public TestAssetInstance WithEmptyGlobalJson() + { + var file = Root.Parent.GetFile("global.json"); + + File.WriteAllText(file.FullName, @"{}"); + + return this; + } + + public TestAssetInstance WithProjectChanges(Action xmlAction) => WithProjectChanges((path, project) => xmlAction(project)); + + public TestAssetInstance WithProjectChanges(Action xmlAction) + { + var projectFileInfos = Root.GetFiles("*.*proj", SearchOption.AllDirectories); + + foreach (var projectFileInfo in projectFileInfos) + { + var projectFile = projectFileInfo.FullName; + var project = XDocument.Load(projectFile); + + xmlAction(projectFile, project); + + using (var file = File.CreateText(projectFile)) + { + project.Save(file); + } + } + + return this; + } + + private static string RebasePath(string path, string oldBaseDirectory, string newBaseDirectory) + { + path = Path.IsPathRooted(path) ? PathUtility.GetRelativePath(PathUtility.EnsureTrailingSlash(oldBaseDirectory), path) : path; + return Path.Combine(newBaseDirectory, path); + } + + private void CopySourceFiles() + { + var filesToCopy = TestAssetInfo.GetSourceFiles(); + foreach (var file in filesToCopy) + { + var newPath = RebasePath(file.FullName, TestAssetInfo.Root.FullName, Root.FullName); + + var newFile = new FileInfo(newPath); + + PathUtility.EnsureDirectoryExists(newFile.Directory.FullName); + + CopyFileAdjustingPaths(file, newFile); + } + } + + private void CopyFileAdjustingPaths(FileInfo source, FileInfo destination) + { + if (string.Equals(source.Name, "nuget.config", StringComparison.OrdinalIgnoreCase)) + { + CopyNugetConfigAdjustingPath(source, destination); + } + else + { + source.CopyTo(destination.FullName); + } + } + + private void CopyNugetConfigAdjustingPath(FileInfo source, FileInfo destination) + { + var doc = XDocument.Load(source.FullName, LoadOptions.PreserveWhitespace); + foreach (var packageSource in doc.Root.Element("packageSources").Elements("add").Attributes("value")) + { + if (!Path.IsPathRooted(packageSource.Value)) + { + string fullPathAtSource = Path.GetFullPath(Path.Combine(source.Directory.FullName, packageSource.Value)); + if (!PathUtility.IsChildOfDirectory(TestAssetInfo.Root.FullName, fullPathAtSource)) + { + packageSource.Value = fullPathAtSource; + } + } + + using (var file = new FileStream( + destination.FullName, + FileMode.CreateNew, + FileAccess.ReadWrite)) + { + doc.Save(file, SaveOptions.None); + } + } + } + + private IEnumerable GetProjectFiles() => Root.GetFiles(TestAssetInfo.ProjectFilePattern, SearchOption.AllDirectories); + + private void Restore(FileInfo projectFile) + { + var restoreArgs = new string[] { "restore", projectFile.FullName }; + + var commandResult = new DotnetCommand() + .Execute(ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(restoreArgs)); + + commandResult.Should().Pass(); + } + + private void RestoreAllProjects() + { + Console.WriteLine($"TestAsset Restore '{TestAssetInfo.AssetName}'"); + + foreach (var projFile in GetProjectFiles()) + { + Restore(projFile); + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInventoryFiles.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInventoryFiles.cs new file mode 100644 index 000000000..cd3e5acac --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetInventoryFiles.cs @@ -0,0 +1,78 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.DotNet.TestFramework +{ + public class TestAssetInventoryFiles + { + private FileInfo _source; + + private FileInfo _restore; + + private FileInfo _build; + + public FileInfo Source + { + get + { + _source.Refresh(); + + return _source; + } + + private set + { + _source = value; + } + } + + public FileInfo Restore + { + get + { + _restore.Refresh(); + + return _restore; + } + + private set + { + _restore = value; + } + } + + public FileInfo Build + { + get + { + _build.Refresh(); + + return _build; + } + + private set + { + _build = value; + } + } + + public TestAssetInventoryFiles(DirectoryInfo inventoryFileDirectory) + { + Source = new FileInfo(Path.Combine(inventoryFileDirectory.FullName, "source.txt")); + + Restore = new FileInfo(Path.Combine(inventoryFileDirectory.FullName, "restore.txt")); + + Build = new FileInfo(Path.Combine(inventoryFileDirectory.FullName, "build.txt")); + } + + public IEnumerable AllInventoryFiles => new List + { + Source, + Restore, + Build + }; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetKinds.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetKinds.cs new file mode 100644 index 000000000..ada0fa9a0 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssetKinds.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.TestFramework +{ + public class TestAssetKinds + { + public static string DesktopTestProjects = "DesktopTestProjects"; + + public static string TestProjects = "TestProjects"; + + public static string NonRestoredTestProjects = "NonRestoredTestProjects"; + + public static string TestPackages = "TestPackages"; + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssets.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssets.cs new file mode 100644 index 000000000..4e154e15c --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestAssets.cs @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Microsoft.DotNet.TestFramework +{ + public class TestAssets + { + private DirectoryInfo _root; + + private FileInfo _dotnetCsprojExe; + + private string _testWorkingFolder; + + public FileInfo DotnetCsprojExe => _dotnetCsprojExe; + + public TestAssets(DirectoryInfo assetsRoot, FileInfo dotnetCsprojExe, string testWorkingFolder) + { + if (assetsRoot == null) + { + throw new ArgumentNullException(nameof(assetsRoot)); + } + + if (dotnetCsprojExe == null) + { + throw new ArgumentNullException(nameof(dotnetCsprojExe)); + } + + if (!assetsRoot.Exists) + { + throw new DirectoryNotFoundException($"Directory not found at '{assetsRoot}'"); + } + + if (!dotnetCsprojExe.Exists) + { + throw new FileNotFoundException("Csproj dotnet executable must exist", dotnetCsprojExe.FullName); + } + + _root = assetsRoot; + + _dotnetCsprojExe = dotnetCsprojExe; + _testWorkingFolder = testWorkingFolder; + } + + public TestAssetInfo Get(string name) => Get(TestAssetKinds.TestProjects, name); + + public TestAssetInfo Get(string kind, string name) + { + var assetDirectory = new DirectoryInfo(Path.Combine(_root.FullName, kind, name)); + + return new TestAssetInfo(assetDirectory, name, this); + } + + public DirectoryInfo CreateTestDirectory(string testProjectName = "", [CallerMemberName] string callingMethod = "", string identifier = "") + { + var testDestination = GetTestDestinationDirectoryPath(testProjectName, callingMethod, identifier); + + var testDirectory = new DirectoryInfo(testDestination); + + testDirectory.EnsureExistsAndEmpty(); + + return testDirectory; + } + + private string GetTestDestinationDirectoryPath(string testProjectName, string callingMethod, string identifier) + { +#if NET451 + string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; +#else + string baseDirectory = AppContext.BaseDirectory; +#endif + // Find the name of the assembly the test comes from based on the the base directory and how the output path has been constructed + string testAssemblyName = new DirectoryInfo(baseDirectory).Parent.Parent.Name; + + string directory = Path.Combine(_testWorkingFolder, testAssemblyName, callingMethod + identifier); + if (!string.IsNullOrEmpty(testProjectName)) + { + directory = Path.Combine(directory, testProjectName); + } + return directory; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs new file mode 100644 index 000000000..c6c0e3240 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs @@ -0,0 +1,156 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.TestFramework; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + /// + /// Base class for all unit test classes. + /// + public abstract class TestBase : IDisposable + { + protected const string DefaultFramework = "netcoreapp1.0"; + protected const string DefaultLibraryFramework = "netstandard1.5"; + protected const string ConsoleLoggerOutputNormal = "--logger console;verbosity=normal"; + private TempRoot _temp; + private static TestAssets s_testAssets; + + static TestBase() + { + // set culture of test process to match CLI sub-processes when the UI language is overriden. + string overriddenUILanguage = Environment.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"); + if (overriddenUILanguage != null) + { + CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(overriddenUILanguage); + } + } + + protected static string RepoRoot => RepoDirectoriesProvider.RepoRoot; + + public static TestAssets TestAssets + { + get + { + if (s_testAssets == null) + { + var assetsRoot = Path.Combine(RepoRoot, "TestAssets"); + + s_testAssets = new TestAssets( + new DirectoryInfo(assetsRoot), + new FileInfo(RepoDirectoriesProvider.DotnetUnderTest), + RepoDirectoriesProvider.TestWorkingFolder); + } + + return s_testAssets; + } + } + + protected TestBase() + { + } + + public static string GetUniqueName() => Guid.NewGuid().ToString("D"); + + public TempRoot Temp + { + get + { + if (_temp == null) + { + _temp = new TempRoot(); + } + + return _temp; + } + } + + public virtual void Dispose() + { + if (_temp != null && !PreserveTemp()) + { + _temp.Dispose(); + } + } + + // Quick-n-dirty way to allow the temp output to be preserved when running tests + private bool PreserveTemp() + { + var val = Environment.GetEnvironmentVariable("DOTNET_TEST_PRESERVE_TEMP"); + return !string.IsNullOrEmpty(val) && ( + string.Equals("true", val, StringComparison.OrdinalIgnoreCase) || + string.Equals("1", val, StringComparison.OrdinalIgnoreCase) || + string.Equals("on", val, StringComparison.OrdinalIgnoreCase)); + } + + protected CommandResult TestExecutable(string outputDir, + string executableName, + string expectedOutput) + { + var executablePath = Path.Combine(outputDir, executableName); + var args = new List(); + + if (IsPortable(executablePath)) + { + args.Add("exec"); + args.Add(ArgumentEscaper.EscapeSingleArg(executablePath)); + + executablePath = RepoDirectoriesProvider.DotnetUnderTest; + } + + var executableCommand = new TestCommand(executablePath); + + var result = executableCommand.ExecuteWithCapturedOutput(string.Join(" ", args)); + + if (!string.IsNullOrEmpty(expectedOutput)) + { + result.Should().HaveStdOut(expectedOutput); + } + result.Should().NotHaveStdErr(); + result.Should().Pass(); + return result; + } + + protected void TestOutputExecutable( + string outputDir, + string executableName, + string expectedOutput, + bool native = false) => TestExecutable(GetCompilationOutputPath(outputDir, native), executableName, expectedOutput); + + protected void TestNativeOutputExecutable(string outputDir, string executableName, string expectedOutput) => TestOutputExecutable(outputDir, executableName, expectedOutput, true); + + protected string GetCompilationOutputPath(string outputDir, bool native) + { + var executablePath = outputDir; + if (native) + { + executablePath = Path.Combine(executablePath, "native"); + } + + return executablePath; + } + + private bool IsPortable(string executablePath) + { + var commandDir = Path.GetDirectoryName(executablePath); + + var runtimeConfigPath = Directory.EnumerateFiles(commandDir) + .FirstOrDefault(x => x.EndsWith("runtimeconfig.json")); + + if (runtimeConfigPath == null) + { + return false; + } + + var runtimeConfig = new RuntimeConfig(runtimeConfigPath); + Console.WriteLine(runtimeConfig.Framework); + return runtimeConfig.IsPortable; + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/UnixOnlyFactAttribute.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/UnixOnlyFactAttribute.cs new file mode 100644 index 000000000..843d0668d --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/UnixOnlyFactAttribute.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class UnixOnlyFactAttribute : FactAttribute + { + public UnixOnlyFactAttribute() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + this.Skip = "This test requires Unix to run"; + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/UnixOnlyTheoryAttribute.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/UnixOnlyTheoryAttribute.cs new file mode 100644 index 000000000..461ea7b36 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/UnixOnlyTheoryAttribute.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class UnixOnlyTheoryAttribute : TheoryAttribute + { + public UnixOnlyTheoryAttribute() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + this.Skip = "This test requires Unix to run"; + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/WindowsOnlyFactAttribute.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/WindowsOnlyFactAttribute.cs new file mode 100644 index 000000000..079a1af90 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/WindowsOnlyFactAttribute.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class WindowsOnlyFactAttribute : FactAttribute + { + public WindowsOnlyFactAttribute() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + this.Skip = "This test requires Windows to run"; + } + } + } +} diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/WindowsOnlyTheoryAttribute.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/WindowsOnlyTheoryAttribute.cs new file mode 100644 index 000000000..98a0267b0 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/WindowsOnlyTheoryAttribute.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class WindowsOnlyTheoryAttribute : TheoryAttribute + { + public WindowsOnlyTheoryAttribute() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + this.Skip = "This test requires Windows to run"; + } + } + } +} diff --git a/test/SdkTests/SdkTests.csproj b/test/SdkTests/SdkTests.csproj new file mode 100644 index 000000000..dfdb8aaa2 --- /dev/null +++ b/test/SdkTests/SdkTests.csproj @@ -0,0 +1,259 @@ + + + + false + false + Library + false + false + false + true + true + true + + + + + + $(MSBuildToolsPath)\Microsoft.CSharp.targets + false + false + $(CoreSdkTargetFramework) + + + + $(ArtifactsBinDir)redist\$(Configuration)\dotnet\ + $(ArtifactsBinDir)redist\$(Configuration)\dotnet-with-previous-runtimes\ + $([MSBuild]::NormalizePath($(ArtifactsTmpDir), 'dotnetSdkTests')) + $(ArtifactsTmpDir)DOTNET_CLI_HOME\ + + + + + + + + + + + + + + + + + + + + + + + + Windows + + + + + + + + + + + + + + + + + + + $(DotnetToTestPath)%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + + $(DotnetToTestPath)shared\Microsoft.NETCore.App\$(RuntimeVersionToInstall) + + + + + powershell -NoLogo -NoProfile -ExecutionPolicy ByPass -Command "& { + $(InstallRuntimeCommand) [Net.ServicePointManager]::SecurityProtocol = 'Tls12, Tls13'; + $(InstallRuntimeCommand) & '$(DotNetRoot)dotnet-install.ps1' + $(InstallRuntimeCommand) -Version $(RuntimeVersionToInstall) + $(InstallRuntimeCommand) -InstallDir $(DotnetToTestPath) + $(InstallRuntimeCommand) -Runtime 'dotnet' + $(InstallRuntimeCommand) -Architecture '$(Architecture)' + $(InstallRuntimeCommand) }" + + + + /bin/bash + $(InstallRuntimeCommand) "$(DotNetRoot)dotnet-install.sh" + $(InstallRuntimeCommand) --version $(RuntimeVersionToInstall) + $(InstallRuntimeCommand) --install-dir "$(DotnetToTestPath)" + $(InstallRuntimeCommand) --runtime "dotnet" + $(InstallRuntimeCommand) --architecture "$(Architecture)" + + + + + + + + + + + + + $(TestExecutionDirectory)\NuGet.config + + + + + + ]]> + + + + + + + + + + + + + + ToolTestName=%(SdkTest.Identity) + + + + + + + + + testSdk$(ToolTestName) + Microsoft.NET.$(ToolTestName).Tests + $(ArtifactsTmpDir)$(ToolCommandName)\ + + + + + + + + $(ToolRunPrefix)set MSBuildSDKsPath=&& + $(ToolRunPrefix)set DOTNET_HOST_PATH=&& + $(ToolRunPrefix)set DOTNET_INSTALLDIR=&& + + + $(ToolRunPrefix)unset MSBuildSDKsPath DOTNET_HOST_PATH DOTNET_INSTALLDIR && + + + + + + $(MicrosoftNETSdkPackageVersion) + $(RedistLayoutPath)dotnet tool install --local $(ToolCommandName) + $(InstallToolCommand) --version $(SdkTestPackageVersion) + + + + + + $(ArtifactsTestResultsDir)$(TestProjectName).xml + $(ArtifactsTestResultsDir)$(TestProjectName).html + $(ArtifactsLogDir)$(TestProjectName).log + + -noautoreporters -noRepoInference + $(TestArgs) -dotnetPath $(DotnetToTestPath)dotnet + $(TestArgs) -testExecutionDirectory $(TestExecutionDirectory) + $(TestArgs) -testConfigFile "$(MSBuildThisFileDirectory)TestConfig.xml" + $(TestArgs) -xml "$(ResultsXmlPath)" + $(TestArgs) -html "$(ResultsHtmlPath)" $(TestRunnerAdditionalArguments) + $(TestArgs) > $(ResultsStdOutPath) 2>&1 + $(TestArgs) -testList SdkIntegrationTests + + + + + $(TestArgs) -testConfigFile "$(MSBuildThisFileDirectory)TestsToSkipStableSDK.xml" + + + + + $(TestArgs) -testConfigFile "$(MSBuildThisFileDirectory)TestsToSkipx86.xml" + + + + + $(TestArgs) -testConfigFile "$(MSBuildThisFileDirectory)TestsToSkipPortableLinux.xml" + + + + + $(TestArgs) -testConfigFile "$(MSBuildThisFileDirectory)TestsToSkipLinux.xml" + + + + + $(TestArgs) -testConfigFile "$(MSBuildThisFileDirectory)TestsToSkipOSX.xml" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/SdkTests/TestConfig.xml b/test/SdkTests/TestConfig.xml new file mode 100644 index 000000000..5dc6368a5 --- /dev/null +++ b/test/SdkTests/TestConfig.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/SdkTests/TestsToSkipLinux.xml b/test/SdkTests/TestsToSkipLinux.xml new file mode 100644 index 000000000..adc3dcbc5 --- /dev/null +++ b/test/SdkTests/TestsToSkipLinux.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/test/SdkTests/TestsToSkipOSX.xml b/test/SdkTests/TestsToSkipOSX.xml new file mode 100644 index 000000000..09330bb1f --- /dev/null +++ b/test/SdkTests/TestsToSkipOSX.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/test/SdkTests/TestsToSkipPortableLinux.xml b/test/SdkTests/TestsToSkipPortableLinux.xml new file mode 100644 index 000000000..734362867 --- /dev/null +++ b/test/SdkTests/TestsToSkipPortableLinux.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/test/SdkTests/TestsToSkipStableSDK.xml b/test/SdkTests/TestsToSkipStableSDK.xml new file mode 100644 index 000000000..e0f5b37b7 --- /dev/null +++ b/test/SdkTests/TestsToSkipStableSDK.xml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/SdkTests/TestsToSkipx86.xml b/test/SdkTests/TestsToSkipx86.xml new file mode 100644 index 000000000..c4cde72c5 --- /dev/null +++ b/test/SdkTests/TestsToSkipx86.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/test/core-sdk-tasks.Tests/CalculateTemplateVerionsTests.cs b/test/core-sdk-tasks.Tests/CalculateTemplateVerionsTests.cs new file mode 100644 index 000000000..f0463b8f9 --- /dev/null +++ b/test/core-sdk-tasks.Tests/CalculateTemplateVerionsTests.cs @@ -0,0 +1,51 @@ +using FluentAssertions; +using Xunit; +using Microsoft.DotNet.Cli.Build; + +namespace EndToEnd +{ + public class CalculateTemplateVersionsTests + { + [Fact] + public void WhenAspNetCoreTemplateMajorVersionLowerthan3ItCanCalculateTemplateVersionsInStableBuilds() + { + var result = CalculateTemplateVersions.Calculate("3.1.0"); + + // The patch is 1 higher than aspnetTemplateVersion due to https://github.com/dotnet/core-sdk/issues/6243 + result.InstallPath.Should().Be("3.1.1"); + result.MajorMinorVersion.Should().Be("3.1"); + result.MajorMinorPatchVersion.Should().Be("3.1.1"); + } + + [Fact] + public void WhenAspNetCoreTemplateMajorVersionLowerthan3ItCanCalculateTemplateVersionsInNonStableBuilds() + { + var result = CalculateTemplateVersions.Calculate("3.0.0-alpha.1.20071.6"); + + result.InstallPath.Should().Be("3.0.1-alpha.1.20071.6"); + result.MajorMinorVersion.Should().Be("3.0"); + result.MajorMinorPatchVersion.Should().Be("3.0.1"); + } + + [Fact] + public void WhenAspNetCoreTemplateMajorVersionHigherthan3ItCanCalculateTemplateVersionsInStableBuilds() + { + var result = CalculateTemplateVersions.Calculate("5.1.0"); + + // The patch align with AspNetCoreTemplateMajorVersion again, since there is no non-deterministic existing ComponentId under Major version 5. + result.InstallPath.Should().Be("5.1.0"); + result.MajorMinorVersion.Should().Be("5.1"); + result.MajorMinorPatchVersion.Should().Be("5.1.0"); + } + + [Fact] + public void WhenAspNetCoreTemplateMajorVersionHigherthan3ItCanCalculateTemplateVersionsInNonStableBuilds() + { + var result = CalculateTemplateVersions.Calculate("5.0.0-alpha.1.20071.6"); + + result.InstallPath.Should().Be("5.0.0-alpha.1.20071.6"); + result.MajorMinorVersion.Should().Be("5.0"); + result.MajorMinorPatchVersion.Should().Be("5.0.0"); + } + } +} diff --git a/test/core-sdk-tasks.Tests/GenerateDefaultRuntimeFrameworkVersionTests.cs b/test/core-sdk-tasks.Tests/GenerateDefaultRuntimeFrameworkVersionTests.cs new file mode 100644 index 000000000..377b19e5a --- /dev/null +++ b/test/core-sdk-tasks.Tests/GenerateDefaultRuntimeFrameworkVersionTests.cs @@ -0,0 +1,28 @@ +using FluentAssertions; +using Xunit; +using Microsoft.DotNet.Cli.Build; + +namespace EndToEnd +{ + public class GenerateDefaultRuntimeFrameworkVersionTests + { + [Theory] + [InlineData("3.0.0-rtm", "3.0.0-rtm")] + [InlineData("3.1.0", "3.1.0")] + [InlineData("10.3.10", "10.3.0")] + [InlineData("1.1.10-prerelease", "1.1.0")] + public void ItGeneratesDefaultVersionBasedOnRuntimePackVersion(string runtimePackVersion, string defaultRuntimeFrameworkVersion) + { + var generateTask = new GenerateDefaultRuntimeFrameworkVersion() + { + RuntimePackVersion = runtimePackVersion + }; + + generateTask + .Execute() + .Should().BeTrue(); + + generateTask.DefaultRuntimeFrameworkVersion.Should().Be(defaultRuntimeFrameworkVersion); + } + } +} diff --git a/test/core-sdk-tasks.Tests/core-sdk-tasks.Tests.csproj b/test/core-sdk-tasks.Tests/core-sdk-tasks.Tests.csproj new file mode 100644 index 000000000..47b6ce517 --- /dev/null +++ b/test/core-sdk-tasks.Tests/core-sdk-tasks.Tests.csproj @@ -0,0 +1,14 @@ + + + $(CoreSdkTargetFramework);net472 + $(CoreSdkTargetFramework) + + + + + + + + + + \ No newline at end of file diff --git a/tools/sdk-readme-table-generator/README.md b/tools/sdk-readme-table-generator/README.md new file mode 100644 index 000000000..f9b06a5ea --- /dev/null +++ b/tools/sdk-readme-table-generator/README.md @@ -0,0 +1,11 @@ +# SDK README table generator + +To generate the table of [SDK per check in build download table](https://github.com/dotnet/installer). + +1. Edit the `inputBranches` in Program.fs when there is a new branch created. Make edits to table.fs if there are changes to the specific items or order you want showing up. +2. Run the program, it will generate the table with updated branch. It is not too smart, so if there is new platform added, we need more to change the program. But it is still better than edit it by hand. +3. Replace the table in the main readme file https://github.com/dotnet/installer/blob/main/README.md + NOTE: The output does not replace the entire file so you have to copy/paste over the pieces that are generated which is the middle of the file +4. Run tests and update the tests + +I wrote it to learn F#, since it is almost the "99 bottle of beer" kata. Please point out places I can improve if you are interested. diff --git a/tools/sdk-readme-table-generator/TableGenerator.Tests/DomainTests.fs b/tools/sdk-readme-table-generator/TableGenerator.Tests/DomainTests.fs new file mode 100644 index 000000000..70953cad0 --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator.Tests/DomainTests.fs @@ -0,0 +1,565 @@ +module TableGenerator.Tests.DomainTests + +open TableGenerator.Shared +open TableGenerator.Reference +open TableGenerator.Table +open TableGenerator.App + +open FsUnit +open Xunit +open System + +[] +let ``it can Shorten main branch Name``() = + let branch = + { GitBranchName = "main" + DisplayName = "main
(5.0.x Runtime)" + AkaMsChannel = Some("net5/dev") } + branchNameShorten branch |> should equal "main" + +[] +let ``it can Shorten release branch Name``() = + let branch = + { GitBranchName = "release/3.1.1xx" + DisplayName = "Release/3.1.1XX
(3.1.x Runtime)" + AkaMsChannel = None} + branchNameShorten branch |> should equal "3.1.1XX" + +[] +let ``it can get major and minor version of a branch``() = + let branch = + { GitBranchName = "release/3.1.1xx" + DisplayName = "Release/3.1.1XX
(3.1.x Runtime)" + AkaMsChannel = None} + getMajorMinor branch + |> should equal + (MajorMinor + ({ Major = 3 + Minor = 1 + Patch = 199 + Release = ""})) + +[] +let ``it can get major and minor version of a preview branch``() = + let branch = + { GitBranchName = "release/5.0.1xx-preview2" + DisplayName = "5.0.100 Preview 2
(5.0 Runtime)" + AkaMsChannel = Some("net5/preview2") } + getMajorMinor branch + |> should equal + (MajorMinor + ({ Major = 5 + Minor = 0 + Patch = 199 + Release = "preview2"})) + +[] +let ``it can get main version of a main branch``() = + let branch = + { GitBranchName = "main" + DisplayName = "main
(5.0.x Runtime)" + AkaMsChannel = None} + getMajorMinor branch |> should equal Main + +[] +let ``it can get bad branch version``() = + let branch = + { GitBranchName = "badbranch" + DisplayName = "" + AkaMsChannel = None} + getMajorMinor branch |> should equal NoVersion + +[] +let ``it can format winx64Reference``() = + let branch = + { GitBranchName = "release/3.1.1xx" + DisplayName = "Release/3.1.1XX
(3.1.x Runtime)" + AkaMsChannel = None} + (winX64ReferenceTemplate branch).Value + |> should equal + """[win-x64-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/win_x64_Release_version_badge.svg +[win-x64-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[win-x64-installer-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.exe +[win-x64-installer-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.exe.sha +[win-x64-zip-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.zip +[win-x64-zip-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.zip.sha""" + +[] +let ``it can format ReferenceWithAkaMslink``() = + let branch = + { GitBranchName = "release/5.0.1xx-preview2" + DisplayName = "5.0.100 Preview 2
(5.0 Runtime)" + AkaMsChannel = Some("net5/preview2") } + + (formatTemplate "win-x64" referenceTemplate branch).Value + |> should equal + """[win-x64-badge-5.0.1XX-preview2]: https://aka.ms/dotnet/net5/preview2/Sdk/win_x64_Release_version_badge.svg +[win-x64-version-5.0.1XX-preview2]: https://aka.ms/dotnet/net5/preview2/Sdk/productCommit-win-x64.txt +[win-x64-installer-5.0.1XX-preview2]: https://aka.ms/dotnet/net5/preview2/Sdk/dotnet-sdk-win-x64.exe +[win-x64-installer-checksum-5.0.1XX-preview2]: https://aka.ms/dotnet/net5/preview2/Sdk/dotnet-sdk-win-x64.exe.sha +[win-x64-zip-5.0.1XX-preview2]: https://aka.ms/dotnet/net5/preview2/Sdk/dotnet-sdk-win-x64.zip +[win-x64-zip-checksum-5.0.1XX-preview2]: https://aka.ms/dotnet/net5/preview2/Sdk/dotnet-sdk-win-x64.zip.sha""" + + +let branches = + [ { GitBranchName = "main" + DisplayName = "main
(5.0.x Runtime)" + AkaMsChannel = None} + { GitBranchName = "release/3.1.1xx" + DisplayName = "Release/3.1.1XX
(3.1.x Runtime)" + AkaMsChannel = None} + { GitBranchName = "release/3.0.1xx" + DisplayName = "Release/3.0.1xx
(3.0.x Runtime)" + AkaMsChannel = None} + { GitBranchName = "release/2.2.2xx" + DisplayName = "Release/2.2.2XX
(2.2.x Runtime)" + AkaMsChannel = None} + { GitBranchName = "release/2.2.1xx" + DisplayName = "Release/2.2.1XX
(2.2.x Runtime)" + AkaMsChannel = None} + { GitBranchName = "release/2.1.6xx" + DisplayName = "Release/2.1.6XX
(2.1.x Runtime)" + AkaMsChannel = None} + { GitBranchName = "release/2.1.5xx" + DisplayName = "Release/2.1.5XX
(2.1.x Runtime)" + AkaMsChannel = None} ] + +[] +let ``it can generate WindowsX64Row``() = + windowsX64Row branches + |> should equal + "| **Windows x64** | [![][win-x64-badge-main]][win-x64-version-main]
[Installer][win-x64-installer-main] - [Checksum][win-x64-installer-checksum-main]
[zip][win-x64-zip-main] - [Checksum][win-x64-zip-checksum-main] | [![][win-x64-badge-3.1.1XX]][win-x64-version-3.1.1XX]
[Installer][win-x64-installer-3.1.1XX] - [Checksum][win-x64-installer-checksum-3.1.1XX]
[zip][win-x64-zip-3.1.1XX] - [Checksum][win-x64-zip-checksum-3.1.1XX] | [![][win-x64-badge-3.0.1XX]][win-x64-version-3.0.1XX]
[Installer][win-x64-installer-3.0.1XX] - [Checksum][win-x64-installer-checksum-3.0.1XX]
[zip][win-x64-zip-3.0.1XX] - [Checksum][win-x64-zip-checksum-3.0.1XX] | [![][win-x64-badge-2.2.2XX]][win-x64-version-2.2.2XX]
[Installer][win-x64-installer-2.2.2XX] - [Checksum][win-x64-installer-checksum-2.2.2XX]
[zip][win-x64-zip-2.2.2XX] - [Checksum][win-x64-zip-checksum-2.2.2XX] | [![][win-x64-badge-2.2.1XX]][win-x64-version-2.2.1XX]
[Installer][win-x64-installer-2.2.1XX] - [Checksum][win-x64-installer-checksum-2.2.1XX]
[zip][win-x64-zip-2.2.1XX] - [Checksum][win-x64-zip-checksum-2.2.1XX] | [![][win-x64-badge-2.1.6XX]][win-x64-version-2.1.6XX]
[Installer][win-x64-installer-2.1.6XX] - [Checksum][win-x64-installer-checksum-2.1.6XX]
[zip][win-x64-zip-2.1.6XX] - [Checksum][win-x64-zip-checksum-2.1.6XX] | [![][win-x64-badge-2.1.5XX]][win-x64-version-2.1.5XX]
[Installer][win-x64-installer-2.1.5XX] - [Checksum][win-x64-installer-checksum-2.1.5XX]
[zip][win-x64-zip-2.1.5XX] - [Checksum][win-x64-zip-checksum-2.1.5XX] |" + +[] +let ``it can generate OsxX64Row``() = + osxX64Row branches + |> should equal + "| **macOS x64** | [![][osx-x64-badge-main]][osx-x64-version-main]
[Installer][osx-x64-installer-main] - [Checksum][osx-x64-installer-checksum-main]
[tar.gz][osx-x64-targz-main] - [Checksum][osx-x64-targz-checksum-main] | [![][osx-x64-badge-3.1.1XX]][osx-x64-version-3.1.1XX]
[Installer][osx-x64-installer-3.1.1XX] - [Checksum][osx-x64-installer-checksum-3.1.1XX]
[tar.gz][osx-x64-targz-3.1.1XX] - [Checksum][osx-x64-targz-checksum-3.1.1XX] | [![][osx-x64-badge-3.0.1XX]][osx-x64-version-3.0.1XX]
[Installer][osx-x64-installer-3.0.1XX] - [Checksum][osx-x64-installer-checksum-3.0.1XX]
[tar.gz][osx-x64-targz-3.0.1XX] - [Checksum][osx-x64-targz-checksum-3.0.1XX] | [![][osx-x64-badge-2.2.2XX]][osx-x64-version-2.2.2XX]
[Installer][osx-x64-installer-2.2.2XX] - [Checksum][osx-x64-installer-checksum-2.2.2XX]
[tar.gz][osx-x64-targz-2.2.2XX] - [Checksum][osx-x64-targz-checksum-2.2.2XX] | [![][osx-x64-badge-2.2.1XX]][osx-x64-version-2.2.1XX]
[Installer][osx-x64-installer-2.2.1XX] - [Checksum][osx-x64-installer-checksum-2.2.1XX]
[tar.gz][osx-x64-targz-2.2.1XX] - [Checksum][osx-x64-targz-checksum-2.2.1XX] | [![][osx-x64-badge-2.1.6XX]][osx-x64-version-2.1.6XX]
[Installer][osx-x64-installer-2.1.6XX] - [Checksum][osx-x64-installer-checksum-2.1.6XX]
[tar.gz][osx-x64-targz-2.1.6XX] - [Checksum][osx-x64-targz-checksum-2.1.6XX] | [![][osx-x64-badge-2.1.5XX]][osx-x64-version-2.1.5XX]
[Installer][osx-x64-installer-2.1.5XX] - [Checksum][osx-x64-installer-checksum-2.1.5XX]
[tar.gz][osx-x64-targz-2.1.5XX] - [Checksum][osx-x64-targz-checksum-2.1.5XX] |" + +[] +let ``it can generate linuxArmRow``() = + linuxArmRow branches + |> should equal + "| **Linux arm** | [![][linux-arm-badge-main]][linux-arm-version-main]
[tar.gz][linux-arm-targz-main] - [Checksum][linux-arm-targz-checksum-main] | [![][linux-arm-badge-3.1.1XX]][linux-arm-version-3.1.1XX]
[tar.gz][linux-arm-targz-3.1.1XX] - [Checksum][linux-arm-targz-checksum-3.1.1XX] | [![][linux-arm-badge-3.0.1XX]][linux-arm-version-3.0.1XX]
[tar.gz][linux-arm-targz-3.0.1XX] - [Checksum][linux-arm-targz-checksum-3.0.1XX] | [![][linux-arm-badge-2.2.2XX]][linux-arm-version-2.2.2XX]
[tar.gz][linux-arm-targz-2.2.2XX] - [Checksum][linux-arm-targz-checksum-2.2.2XX] | [![][linux-arm-badge-2.2.1XX]][linux-arm-version-2.2.1XX]
[tar.gz][linux-arm-targz-2.2.1XX] - [Checksum][linux-arm-targz-checksum-2.2.1XX] | [![][linux-arm-badge-2.1.6XX]][linux-arm-version-2.1.6XX]
[tar.gz][linux-arm-targz-2.1.6XX] - [Checksum][linux-arm-targz-checksum-2.1.6XX] | [![][linux-arm-badge-2.1.5XX]][linux-arm-version-2.1.5XX]
[tar.gz][linux-arm-targz-2.1.5XX] - [Checksum][linux-arm-targz-checksum-2.1.5XX] |" + +[] +let ``it can generate windowsArm``() = + windowsArmRow branches + |> should equal + "| **Windows arm** | **N/A** | [![][win-arm-badge-3.1.1XX]][win-arm-version-3.1.1XX]
[zip][win-arm-zip-3.1.1XX] - [Checksum][win-arm-zip-checksum-3.1.1XX] | [![][win-arm-badge-3.0.1XX]][win-arm-version-3.0.1XX]
[zip][win-arm-zip-3.0.1XX] - [Checksum][win-arm-zip-checksum-3.0.1XX] | [![][win-arm-badge-2.2.2XX]][win-arm-version-2.2.2XX]
[zip][win-arm-zip-2.2.2XX] - [Checksum][win-arm-zip-checksum-2.2.2XX] | [![][win-arm-badge-2.2.1XX]][win-arm-version-2.2.1XX]
[zip][win-arm-zip-2.2.1XX] - [Checksum][win-arm-zip-checksum-2.2.1XX] | [![][win-arm-badge-2.1.6XX]][win-arm-version-2.1.6XX]
[zip][win-arm-zip-2.1.6XX] - [Checksum][win-arm-zip-checksum-2.1.6XX] | [![][win-arm-badge-2.1.5XX]][win-arm-version-2.1.5XX]
[zip][win-arm-zip-2.1.5XX] - [Checksum][win-arm-zip-checksum-2.1.5XX] |" + + +[] +let ``it can generate windowsArm with preview branch``() = + let inputBranches = + [ { GitBranchName = "main" + DisplayName = "main
(5.0.x Runtime)" + AkaMsChannel = Some("net5/dev") } + { GitBranchName = "release/5.0.1xx-preview4" + DisplayName = "5.0.100 Preview 4
(5.0 Runtime)" + AkaMsChannel = Some("net5/preview4") }] + windowsArmRow inputBranches + |> should equal + "| **Windows arm** | **N/A** | **N/A** |" + +[] +let ``it can generate windowsArm64``() = + windowsArm64Row branches + |> should equal + "| **Windows arm64** | [![][win-arm64-badge-main]][win-arm64-version-main]
[Installer][win-arm64-installer-main] - [Checksum][win-arm64-installer-checksum-main]
[zip][win-arm64-zip-main] | **N/A** | **N/A** | **N/A** | **N/A** | **N/A** | **N/A** |" + +let branches2 = + [ { GitBranchName = "main" + DisplayName = "main
(6.0.x Runtime)" + AkaMsChannel = Some("net6/dev") } + { GitBranchName = "release/5.0.1xx-rtm" + DisplayName = "5.0.100 RTM
(5.0 Runtime)" + AkaMsChannel = Some("net5/5.0.1xx/daily") } + { GitBranchName = "release/3.1.4xx" + DisplayName = "Release/3.1.4XX
(3.1.x Runtime)" + AkaMsChannel = None }] + +[] +let ``it can generate linuxMuslRowArm``() = + linuxMuslRowArm branches2 + |> should equal + "| **Linux-musl-arm** | [![][linux-musl-arm-badge-main]][linux-musl-arm-version-main]
[tar.gz][linux-musl-arm-targz-main] - [Checksum][linux-musl-arm-targz-checksum-main] | **N/A** | **N/A** |" + +[] +let ``it can generate linuxMuslRowArm64``() = + linuxMuslRowArm64 branches2 + |> should equal + "| **Linux-musl-arm64** | [![][linux-musl-arm64-badge-main]][linux-musl-arm64-version-main]
[tar.gz][linux-musl-arm64-targz-main] - [Checksum][linux-musl-arm64-targz-checksum-main] | **N/A** | **N/A** |" + + +[] +let ``it can generate titleRow``() = + titleRow branches + |> should equal + "| Platform | main
(5.0.x Runtime) | Release/3.1.1XX
(3.1.x Runtime) | Release/3.0.1xx
(3.0.x Runtime) | Release/2.2.2XX
(2.2.x Runtime) | Release/2.2.1XX
(2.2.x Runtime) | Release/2.1.6XX
(2.1.x Runtime) | Release/2.1.5XX
(2.1.x Runtime) |" + +[] +let ``it can generate separator``() = + separator branches + |> should equal + "| :--------- | :----------: | :----------: | :----------: | :----------: | :----------: | :----------: | :----------: |" +// pinning tests https://wiki.c2.com/?PinningTests + +[] +let ``pinning tests for readme in main 11/02/2020``() = + let inputBranches = + [ { GitBranchName = "main" + DisplayName = "main
(6.0.x Runtime)" + AkaMsChannel = Some("6.0/daily") } + { GitBranchName = "release/5.0.1xx-rtm" + DisplayName = "5.0.100 RTM
(5.0 Runtime)" + AkaMsChannel = Some("net5/5.0.1xx/daily") } + { GitBranchName = "release/5.0.1xx-rc2" + DisplayName = "5.0.100 RC2
(5.0 Runtime)" + AkaMsChannel = Some("net5/rc2") } + { GitBranchName = "release/3.1.4xx" + DisplayName = "Release/3.1.4XX
(3.1.x Runtime)" + AkaMsChannel = None } + { GitBranchName = "release/3.1.1xx" + DisplayName = "Release/3.1.1XX
(3.1.x Runtime)" + AkaMsChannel = None }] + + wholeTable inputBranches + |> should equal + """| Platform | main
(6.0.x Runtime) | 5.0.100 RTM
(5.0 Runtime) | 5.0.100 RC2
(5.0 Runtime) | Release/3.1.4XX
(3.1.x Runtime) | Release/3.1.1XX
(3.1.x Runtime) | +| :--------- | :----------: | :----------: | :----------: | :----------: | :----------: | +| **Windows x64** | [![][win-x64-badge-main]][win-x64-version-main]
[Installer][win-x64-installer-main] - [Checksum][win-x64-installer-checksum-main]
[zip][win-x64-zip-main] - [Checksum][win-x64-zip-checksum-main] | [![][win-x64-badge-5.0.1XX-rtm]][win-x64-version-5.0.1XX-rtm]
[Installer][win-x64-installer-5.0.1XX-rtm] - [Checksum][win-x64-installer-checksum-5.0.1XX-rtm]
[zip][win-x64-zip-5.0.1XX-rtm] - [Checksum][win-x64-zip-checksum-5.0.1XX-rtm] | [![][win-x64-badge-5.0.1XX-rc2]][win-x64-version-5.0.1XX-rc2]
[Installer][win-x64-installer-5.0.1XX-rc2] - [Checksum][win-x64-installer-checksum-5.0.1XX-rc2]
[zip][win-x64-zip-5.0.1XX-rc2] - [Checksum][win-x64-zip-checksum-5.0.1XX-rc2] | [![][win-x64-badge-3.1.4XX]][win-x64-version-3.1.4XX]
[Installer][win-x64-installer-3.1.4XX] - [Checksum][win-x64-installer-checksum-3.1.4XX]
[zip][win-x64-zip-3.1.4XX] - [Checksum][win-x64-zip-checksum-3.1.4XX] | [![][win-x64-badge-3.1.1XX]][win-x64-version-3.1.1XX]
[Installer][win-x64-installer-3.1.1XX] - [Checksum][win-x64-installer-checksum-3.1.1XX]
[zip][win-x64-zip-3.1.1XX] - [Checksum][win-x64-zip-checksum-3.1.1XX] | +| **Windows x86** | [![][win-x86-badge-main]][win-x86-version-main]
[Installer][win-x86-installer-main] - [Checksum][win-x86-installer-checksum-main]
[zip][win-x86-zip-main] - [Checksum][win-x86-zip-checksum-main] | [![][win-x86-badge-5.0.1XX-rtm]][win-x86-version-5.0.1XX-rtm]
[Installer][win-x86-installer-5.0.1XX-rtm] - [Checksum][win-x86-installer-checksum-5.0.1XX-rtm]
[zip][win-x86-zip-5.0.1XX-rtm] - [Checksum][win-x86-zip-checksum-5.0.1XX-rtm] | [![][win-x86-badge-5.0.1XX-rc2]][win-x86-version-5.0.1XX-rc2]
[Installer][win-x86-installer-5.0.1XX-rc2] - [Checksum][win-x86-installer-checksum-5.0.1XX-rc2]
[zip][win-x86-zip-5.0.1XX-rc2] - [Checksum][win-x86-zip-checksum-5.0.1XX-rc2] | [![][win-x86-badge-3.1.4XX]][win-x86-version-3.1.4XX]
[Installer][win-x86-installer-3.1.4XX] - [Checksum][win-x86-installer-checksum-3.1.4XX]
[zip][win-x86-zip-3.1.4XX] - [Checksum][win-x86-zip-checksum-3.1.4XX] | [![][win-x86-badge-3.1.1XX]][win-x86-version-3.1.1XX]
[Installer][win-x86-installer-3.1.1XX] - [Checksum][win-x86-installer-checksum-3.1.1XX]
[zip][win-x86-zip-3.1.1XX] - [Checksum][win-x86-zip-checksum-3.1.1XX] | +| **Windows arm** | **N/A** | **N/A** | **N/A** | [![][win-arm-badge-3.1.4XX]][win-arm-version-3.1.4XX]
[zip][win-arm-zip-3.1.4XX] - [Checksum][win-arm-zip-checksum-3.1.4XX] | [![][win-arm-badge-3.1.1XX]][win-arm-version-3.1.1XX]
[zip][win-arm-zip-3.1.1XX] - [Checksum][win-arm-zip-checksum-3.1.1XX] | +| **Windows arm64** | [![][win-arm64-badge-main]][win-arm64-version-main]
[Installer][win-arm64-installer-main] - [Checksum][win-arm64-installer-checksum-main]
[zip][win-arm64-zip-main] | [![][win-arm64-badge-5.0.1XX-rtm]][win-arm64-version-5.0.1XX-rtm]
[Installer][win-arm64-installer-5.0.1XX-rtm] - [Checksum][win-arm64-installer-checksum-5.0.1XX-rtm]
[zip][win-arm64-zip-5.0.1XX-rtm] | [![][win-arm64-badge-5.0.1XX-rc2]][win-arm64-version-5.0.1XX-rc2]
[Installer][win-arm64-installer-5.0.1XX-rc2] - [Checksum][win-arm64-installer-checksum-5.0.1XX-rc2]
[zip][win-arm64-zip-5.0.1XX-rc2] | **N/A** | **N/A** | +| **macOS x64** | [![][osx-x64-badge-main]][osx-x64-version-main]
[Installer][osx-x64-installer-main] - [Checksum][osx-x64-installer-checksum-main]
[tar.gz][osx-x64-targz-main] - [Checksum][osx-x64-targz-checksum-main] | [![][osx-x64-badge-5.0.1XX-rtm]][osx-x64-version-5.0.1XX-rtm]
[Installer][osx-x64-installer-5.0.1XX-rtm] - [Checksum][osx-x64-installer-checksum-5.0.1XX-rtm]
[tar.gz][osx-x64-targz-5.0.1XX-rtm] - [Checksum][osx-x64-targz-checksum-5.0.1XX-rtm] | [![][osx-x64-badge-5.0.1XX-rc2]][osx-x64-version-5.0.1XX-rc2]
[Installer][osx-x64-installer-5.0.1XX-rc2] - [Checksum][osx-x64-installer-checksum-5.0.1XX-rc2]
[tar.gz][osx-x64-targz-5.0.1XX-rc2] - [Checksum][osx-x64-targz-checksum-5.0.1XX-rc2] | [![][osx-x64-badge-3.1.4XX]][osx-x64-version-3.1.4XX]
[Installer][osx-x64-installer-3.1.4XX] - [Checksum][osx-x64-installer-checksum-3.1.4XX]
[tar.gz][osx-x64-targz-3.1.4XX] - [Checksum][osx-x64-targz-checksum-3.1.4XX] | [![][osx-x64-badge-3.1.1XX]][osx-x64-version-3.1.1XX]
[Installer][osx-x64-installer-3.1.1XX] - [Checksum][osx-x64-installer-checksum-3.1.1XX]
[tar.gz][osx-x64-targz-3.1.1XX] - [Checksum][osx-x64-targz-checksum-3.1.1XX] | +| **macOS arm64** | [![][osx-arm64-badge-main]][osx-arm64-version-main]
[Installer][osx-arm64-installer-main] - [Checksum][osx-arm64-installer-checksum-main]
[tar.gz][osx-arm64-targz-main] - [Checksum][osx-arm64-targz-checksum-main] | **N/A** | **N/A** | **N/A** | **N/A** | +| **Linux x64** | [![][linux-badge-main]][linux-version-main]
[DEB Installer][linux-DEB-installer-main] - [Checksum][linux-DEB-installer-checksum-main]
[RPM Installer][linux-RPM-installer-main] - [Checksum][linux-RPM-installer-checksum-main]
_see installer note below_1
[tar.gz][linux-targz-main] - [Checksum][linux-targz-checksum-main] | [![][linux-badge-5.0.1XX-rtm]][linux-version-5.0.1XX-rtm]
[DEB Installer][linux-DEB-installer-5.0.1XX-rtm] - [Checksum][linux-DEB-installer-checksum-5.0.1XX-rtm]
[RPM Installer][linux-RPM-installer-5.0.1XX-rtm] - [Checksum][linux-RPM-installer-checksum-5.0.1XX-rtm]
_see installer note below_1
[tar.gz][linux-targz-5.0.1XX-rtm] - [Checksum][linux-targz-checksum-5.0.1XX-rtm] | [![][linux-badge-5.0.1XX-rc2]][linux-version-5.0.1XX-rc2]
[DEB Installer][linux-DEB-installer-5.0.1XX-rc2] - [Checksum][linux-DEB-installer-checksum-5.0.1XX-rc2]
[RPM Installer][linux-RPM-installer-5.0.1XX-rc2] - [Checksum][linux-RPM-installer-checksum-5.0.1XX-rc2]
_see installer note below_1
[tar.gz][linux-targz-5.0.1XX-rc2] - [Checksum][linux-targz-checksum-5.0.1XX-rc2] | [![][linux-badge-3.1.4XX]][linux-version-3.1.4XX]
[DEB Installer][linux-DEB-installer-3.1.4XX] - [Checksum][linux-DEB-installer-checksum-3.1.4XX]
[RPM Installer][linux-RPM-installer-3.1.4XX] - [Checksum][linux-RPM-installer-checksum-3.1.4XX]
_see installer note below_1
[tar.gz][linux-targz-3.1.4XX] - [Checksum][linux-targz-checksum-3.1.4XX] | [![][linux-badge-3.1.1XX]][linux-version-3.1.1XX]
[DEB Installer][linux-DEB-installer-3.1.1XX] - [Checksum][linux-DEB-installer-checksum-3.1.1XX]
[RPM Installer][linux-RPM-installer-3.1.1XX] - [Checksum][linux-RPM-installer-checksum-3.1.1XX]
_see installer note below_1
[tar.gz][linux-targz-3.1.1XX] - [Checksum][linux-targz-checksum-3.1.1XX] | +| **Linux arm** | [![][linux-arm-badge-main]][linux-arm-version-main]
[tar.gz][linux-arm-targz-main] - [Checksum][linux-arm-targz-checksum-main] | [![][linux-arm-badge-5.0.1XX-rtm]][linux-arm-version-5.0.1XX-rtm]
[tar.gz][linux-arm-targz-5.0.1XX-rtm] - [Checksum][linux-arm-targz-checksum-5.0.1XX-rtm] | [![][linux-arm-badge-5.0.1XX-rc2]][linux-arm-version-5.0.1XX-rc2]
[tar.gz][linux-arm-targz-5.0.1XX-rc2] - [Checksum][linux-arm-targz-checksum-5.0.1XX-rc2] | [![][linux-arm-badge-3.1.4XX]][linux-arm-version-3.1.4XX]
[tar.gz][linux-arm-targz-3.1.4XX] - [Checksum][linux-arm-targz-checksum-3.1.4XX] | [![][linux-arm-badge-3.1.1XX]][linux-arm-version-3.1.1XX]
[tar.gz][linux-arm-targz-3.1.1XX] - [Checksum][linux-arm-targz-checksum-3.1.1XX] | +| **Linux arm64** | [![][linux-arm64-badge-main]][linux-arm64-version-main]
[tar.gz][linux-arm64-targz-main] - [Checksum][linux-arm64-targz-checksum-main] | [![][linux-arm64-badge-5.0.1XX-rtm]][linux-arm64-version-5.0.1XX-rtm]
[tar.gz][linux-arm64-targz-5.0.1XX-rtm] - [Checksum][linux-arm64-targz-checksum-5.0.1XX-rtm] | [![][linux-arm64-badge-5.0.1XX-rc2]][linux-arm64-version-5.0.1XX-rc2]
[tar.gz][linux-arm64-targz-5.0.1XX-rc2] - [Checksum][linux-arm64-targz-checksum-5.0.1XX-rc2] | [![][linux-arm64-badge-3.1.4XX]][linux-arm64-version-3.1.4XX]
[tar.gz][linux-arm64-targz-3.1.4XX] - [Checksum][linux-arm64-targz-checksum-3.1.4XX] | [![][linux-arm64-badge-3.1.1XX]][linux-arm64-version-3.1.1XX]
[tar.gz][linux-arm64-targz-3.1.1XX] - [Checksum][linux-arm64-targz-checksum-3.1.1XX] | +| **Linux-musl-x64** | [![][linux-musl-x64-badge-main]][linux-musl-x64-version-main]
[tar.gz][linux-musl-x64-targz-main] - [Checksum][linux-musl-x64-targz-checksum-main] | [![][linux-musl-x64-badge-5.0.1XX-rtm]][linux-musl-x64-version-5.0.1XX-rtm]
[tar.gz][linux-musl-x64-targz-5.0.1XX-rtm] - [Checksum][linux-musl-x64-targz-checksum-5.0.1XX-rtm] | [![][linux-musl-x64-badge-5.0.1XX-rc2]][linux-musl-x64-version-5.0.1XX-rc2]
[tar.gz][linux-musl-x64-targz-5.0.1XX-rc2] - [Checksum][linux-musl-x64-targz-checksum-5.0.1XX-rc2] | [![][linux-musl-x64-badge-3.1.4XX]][linux-musl-x64-version-3.1.4XX]
[tar.gz][linux-musl-x64-targz-3.1.4XX] - [Checksum][linux-musl-x64-targz-checksum-3.1.4XX] | [![][linux-musl-x64-badge-3.1.1XX]][linux-musl-x64-version-3.1.1XX]
[tar.gz][linux-musl-x64-targz-3.1.1XX] - [Checksum][linux-musl-x64-targz-checksum-3.1.1XX] | +| **Linux-musl-arm** | [![][linux-musl-arm-badge-main]][linux-musl-arm-version-main]
[tar.gz][linux-musl-arm-targz-main] - [Checksum][linux-musl-arm-targz-checksum-main] | **N/A** | **N/A** | **N/A** | **N/A** | +| **Linux-musl-arm64** | [![][linux-musl-arm64-badge-main]][linux-musl-arm64-version-main]
[tar.gz][linux-musl-arm64-targz-main] - [Checksum][linux-musl-arm64-targz-checksum-main] | **N/A** | **N/A** | **N/A** | **N/A** | +| **RHEL 6** | **N/A** | **N/A** | **N/A** | [![][rhel-6-badge-3.1.4XX]][rhel-6-version-3.1.4XX]
[tar.gz][rhel-6-targz-3.1.4XX] - [Checksum][rhel-6-targz-checksum-3.1.4XX] | [![][rhel-6-badge-3.1.1XX]][rhel-6-version-3.1.1XX]
[tar.gz][rhel-6-targz-3.1.1XX] - [Checksum][rhel-6-targz-checksum-3.1.1XX] | + +Reference notes: +> **1**: Our Debian packages are put together slightly differently than the other OS specific installers. Instead of combining everything, we have separate component packages that depend on each other. If you're installing the SDK from the .deb file (via dpkg or similar), then you'll need to install the corresponding dependencies first: +> * [Host, Host FX Resolver, and Shared Framework](https://github.com/dotnet/runtime/blob/main/docs/project/dogfooding.md#nightly-builds-table) +> * [ASP.NET Core Shared Framework](https://github.com/aspnet/AspNetCore/blob/main/docs/DailyBuilds.md) + +.NET Core SDK 2.x downloads can be found here: [.NET Core SDK 2.x Installers and Binaries](Downloads2.x.md) + +[win-x64-badge-main]: https://aka.ms/dotnet/6.0/daily/win_x64_Release_version_badge.svg +[win-x64-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-win-x64.txt +[win-x64-installer-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.exe +[win-x64-installer-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.exe.sha +[win-x64-zip-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.zip +[win-x64-zip-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.zip.sha + +[win-x64-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/win_x64_Release_version_badge.svg +[win-x64-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-win-x64.txt +[win-x64-installer-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x64.exe +[win-x64-installer-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x64.exe.sha +[win-x64-zip-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x64.zip +[win-x64-zip-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x64.zip.sha + +[win-x64-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/win_x64_Release_version_badge.svg +[win-x64-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-win-x64.txt +[win-x64-installer-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x64.exe +[win-x64-installer-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x64.exe.sha +[win-x64-zip-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x64.zip +[win-x64-zip-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x64.zip.sha + +[win-x64-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/win_x64_Release_version_badge.svg +[win-x64-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[win-x64-installer-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x64.exe +[win-x64-installer-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x64.exe.sha +[win-x64-zip-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x64.zip +[win-x64-zip-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x64.zip.sha + +[win-x64-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/win_x64_Release_version_badge.svg +[win-x64-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[win-x64-installer-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.exe +[win-x64-installer-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.exe.sha +[win-x64-zip-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.zip +[win-x64-zip-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x64.zip.sha + +[win-x86-badge-main]: https://aka.ms/dotnet/6.0/daily/win_x86_Release_version_badge.svg +[win-x86-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-win-x86.txt +[win-x86-installer-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x86.exe +[win-x86-installer-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x86.exe.sha +[win-x86-zip-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x86.zip +[win-x86-zip-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x86.zip.sha + +[win-x86-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/win_x86_Release_version_badge.svg +[win-x86-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-win-x86.txt +[win-x86-installer-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x86.exe +[win-x86-installer-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x86.exe.sha +[win-x86-zip-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x86.zip +[win-x86-zip-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-x86.zip.sha + +[win-x86-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/win_x86_Release_version_badge.svg +[win-x86-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-win-x86.txt +[win-x86-installer-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x86.exe +[win-x86-installer-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x86.exe.sha +[win-x86-zip-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x86.zip +[win-x86-zip-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-x86.zip.sha + +[win-x86-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/win_x86_Release_version_badge.svg +[win-x86-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[win-x86-installer-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x86.exe +[win-x86-installer-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x86.exe.sha +[win-x86-zip-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x86.zip +[win-x86-zip-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-x86.zip.sha + +[win-x86-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/win_x86_Release_version_badge.svg +[win-x86-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[win-x86-installer-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x86.exe +[win-x86-installer-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x86.exe.sha +[win-x86-zip-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x86.zip +[win-x86-zip-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-x86.zip.sha + +[osx-x64-badge-main]: https://aka.ms/dotnet/6.0/daily/osx_x64_Release_version_badge.svg +[osx-x64-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-osx-x64.txt +[osx-x64-installer-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-x64.pkg +[osx-x64-installer-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-x64.pkg.sha +[osx-x64-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-x64.tar.gz +[osx-x64-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha + +[osx-x64-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/osx_x64_Release_version_badge.svg +[osx-x64-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-osx-x64.txt +[osx-x64-installer-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-osx-x64.pkg +[osx-x64-installer-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-osx-x64.pkg.sha +[osx-x64-targz-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-osx-x64.tar.gz +[osx-x64-targz-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-osx-x64.pkg.tar.gz.sha + +[osx-x64-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/osx_x64_Release_version_badge.svg +[osx-x64-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-osx-x64.txt +[osx-x64-installer-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-osx-x64.pkg +[osx-x64-installer-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-osx-x64.pkg.sha +[osx-x64-targz-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-osx-x64.tar.gz +[osx-x64-targz-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-osx-x64.pkg.tar.gz.sha + +[osx-x64-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/osx_x64_Release_version_badge.svg +[osx-x64-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[osx-x64-installer-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-osx-x64.pkg +[osx-x64-installer-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-osx-x64.pkg.sha +[osx-x64-targz-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-osx-x64.tar.gz +[osx-x64-targz-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-osx-x64.tar.gz.sha + +[osx-x64-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/osx_x64_Release_version_badge.svg +[osx-x64-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[osx-x64-installer-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-osx-x64.pkg +[osx-x64-installer-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-osx-x64.pkg.sha +[osx-x64-targz-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-osx-x64.tar.gz +[osx-x64-targz-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-osx-x64.tar.gz.sha + +[osx-arm64-badge-main]: https://aka.ms/dotnet/6.0/daily/osx_arm64_Release_version_badge.svg +[osx-arm64-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-osx-arm64.txt +[osx-arm64-installer-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-arm64.pkg +[osx-arm64-installer-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-arm64.pkg.sha +[osx-arm64-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-arm64.tar.gz +[osx-arm64-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha + +[linux-badge-main]: https://aka.ms/dotnet/6.0/daily/linux_x64_Release_version_badge.svg +[linux-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-linux-x64.txt +[linux-DEB-installer-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-x64.deb +[linux-DEB-installer-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-x64.deb.sha +[linux-RPM-installer-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-x64.rpm +[linux-RPM-installer-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-x64.rpm.sha +[linux-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-x64.tar.gz +[linux-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-x64.tar.gz.sha + +[linux-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/linux_x64_Release_version_badge.svg +[linux-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-linux-x64.txt +[linux-DEB-installer-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-x64.deb +[linux-DEB-installer-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-x64.deb.sha +[linux-RPM-installer-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-x64.rpm +[linux-RPM-installer-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-x64.rpm.sha +[linux-targz-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-x64.tar.gz +[linux-targz-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-x64.tar.gz.sha + +[linux-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/linux_x64_Release_version_badge.svg +[linux-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-linux-x64.txt +[linux-DEB-installer-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-x64.deb +[linux-DEB-installer-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-x64.deb.sha +[linux-RPM-installer-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-x64.rpm +[linux-RPM-installer-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-x64.rpm.sha +[linux-targz-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-x64.tar.gz +[linux-targz-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-x64.tar.gz.sha + +[linux-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/linux_x64_Release_version_badge.svg +[linux-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[linux-DEB-installer-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-x64.deb +[linux-DEB-installer-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-x64.deb.sha +[linux-RPM-installer-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-x64.rpm +[linux-RPM-installer-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-x64.rpm.sha +[linux-targz-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-x64.tar.gz +[linux-targz-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-x64.tar.gz.sha + +[linux-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/linux_x64_Release_version_badge.svg +[linux-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[linux-DEB-installer-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-x64.deb +[linux-DEB-installer-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-x64.deb.sha +[linux-RPM-installer-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-x64.rpm +[linux-RPM-installer-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-x64.rpm.sha +[linux-targz-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-x64.tar.gz +[linux-targz-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-x64.tar.gz.sha + +[linux-arm-badge-main]: https://aka.ms/dotnet/6.0/daily/linux_arm_Release_version_badge.svg +[linux-arm-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-linux-arm.txt +[linux-arm-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-arm.tar.gz +[linux-arm-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-arm.tar.gz.sha + +[linux-arm-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/linux_arm_Release_version_badge.svg +[linux-arm-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-linux-arm.txt +[linux-arm-targz-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-arm.tar.gz +[linux-arm-targz-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-arm.tar.gz.sha + +[linux-arm-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/linux_arm_Release_version_badge.svg +[linux-arm-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-linux-arm.txt +[linux-arm-targz-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-arm.tar.gz +[linux-arm-targz-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-arm.tar.gz.sha + +[linux-arm-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/linux_arm_Release_version_badge.svg +[linux-arm-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[linux-arm-targz-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-arm.tar.gz +[linux-arm-targz-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-arm.tar.gz.sha + +[linux-arm-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/linux_arm_Release_version_badge.svg +[linux-arm-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[linux-arm-targz-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-arm.tar.gz +[linux-arm-targz-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-arm.tar.gz.sha + +[linux-arm64-badge-main]: https://aka.ms/dotnet/6.0/daily/linux_arm64_Release_version_badge.svg +[linux-arm64-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-linux-arm64.txt +[linux-arm64-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-arm64.tar.gz +[linux-arm64-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-arm64.tar.gz.sha + +[linux-arm64-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/linux_arm64_Release_version_badge.svg +[linux-arm64-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-linux-arm64.txt +[linux-arm64-targz-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-arm64.tar.gz +[linux-arm64-targz-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-arm64.tar.gz.sha + +[linux-arm64-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/linux_arm64_Release_version_badge.svg +[linux-arm64-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-linux-arm64.txt +[linux-arm64-targz-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-arm64.tar.gz +[linux-arm64-targz-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-arm64.tar.gz.sha + +[linux-arm64-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/linux_arm64_Release_version_badge.svg +[linux-arm64-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[linux-arm64-targz-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-arm64.tar.gz +[linux-arm64-targz-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-arm64.tar.gz.sha + +[linux-arm64-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/linux_arm64_Release_version_badge.svg +[linux-arm64-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[linux-arm64-targz-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-arm64.tar.gz +[linux-arm64-targz-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-arm64.tar.gz.sha + +[rhel-6-badge-main]: https://aka.ms/dotnet/6.0/daily/rhel.6_x64_Release_version_badge.svg +[rhel-6-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-rhel.6-x64.txt +[rhel-6-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-rhel.6-x64.tar.gz +[rhel-6-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-rhel.6-x64.tar.gz.sha + +[rhel-6-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/rhel.6_x64_Release_version_badge.svg +[rhel-6-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-rhel.6-x64.txt +[rhel-6-targz-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-rhel.6-x64.tar.gz +[rhel-6-targz-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-rhel.6-x64.tar.gz.sha + +[rhel-6-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/rhel.6_x64_Release_version_badge.svg +[rhel-6-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-rhel.6-x64.txt +[rhel-6-targz-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-rhel.6-x64.tar.gz +[rhel-6-targz-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-rhel.6-x64.tar.gz.sha + +[rhel-6-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/rhel.6_x64_Release_version_badge.svg +[rhel-6-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[rhel-6-targz-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-rhel.6-x64.tar.gz +[rhel-6-targz-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-rhel.6-x64.tar.gz.sha + +[rhel-6-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/rhel.6_x64_Release_version_badge.svg +[rhel-6-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[rhel-6-targz-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-rhel.6-x64.tar.gz +[rhel-6-targz-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-rhel.6-x64.tar.gz.sha + +[linux-musl-x64-badge-main]: https://aka.ms/dotnet/6.0/daily/linux_musl_x64_Release_version_badge.svg +[linux-musl-x64-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-linux-musl-x64.txt +[linux-musl-x64-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha + +[linux-musl-x64-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/linux_musl_x64_Release_version_badge.svg +[linux-musl-x64-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-linux-musl-x64.txt +[linux-musl-x64-targz-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-linux-musl-x64.tar.gz.sha + +[linux-musl-x64-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/linux_musl_x64_Release_version_badge.svg +[linux-musl-x64-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-linux-musl-x64.txt +[linux-musl-x64-targz-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-linux-musl-x64.tar.gz.sha + +[linux-musl-x64-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/linux_musl_x64_Release_version_badge.svg +[linux-musl-x64-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[linux-musl-x64-targz-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-linux-musl-x64.tar.gz.sha + +[linux-musl-x64-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/linux_musl_x64_Release_version_badge.svg +[linux-musl-x64-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[linux-musl-x64-targz-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-linux-musl-x64.tar.gz.sha + +[linux-musl-arm-badge-main]: https://aka.ms/dotnet/6.0/daily/linux_musl_arm_Release_version_badge.svg +[linux-musl-arm-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-linux-musl-arm.txt +[linux-musl-arm-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-musl-arm.tar.gz +[linux-musl-arm-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha + +[linux-musl-arm64-badge-main]: https://aka.ms/dotnet/6.0/daily/linux_musl_arm64_Release_version_badge.svg +[linux-musl-arm64-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-linux-musl-arm64.txt +[linux-musl-arm64-targz-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-musl-arm64.tar.gz +[linux-musl-arm64-targz-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha + +[win-arm-badge-main]: https://aka.ms/dotnet/6.0/daily/win_arm_Release_version_badge.svg +[win-arm-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-win-arm.txt +[win-arm-zip-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-arm.zip +[win-arm-zip-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-arm.zip.sha + +[win-arm-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/win_arm_Release_version_badge.svg +[win-arm-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-win-arm.txt +[win-arm-zip-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-arm.zip +[win-arm-zip-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-arm.zip.sha + +[win-arm-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/win_arm_Release_version_badge.svg +[win-arm-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-win-arm.txt +[win-arm-zip-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-arm.zip +[win-arm-zip-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-arm.zip.sha + +[win-arm-badge-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/win_arm_Release_version_badge.svg +[win-arm-version-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/latest.version +[win-arm-zip-3.1.4XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-arm.zip +[win-arm-zip-checksum-3.1.4XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.4xx/dotnet-sdk-latest-win-arm.zip.sha + +[win-arm-badge-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/win_arm_Release_version_badge.svg +[win-arm-version-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/latest.version +[win-arm-zip-3.1.1XX]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-arm.zip +[win-arm-zip-checksum-3.1.1XX]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/release/3.1.1xx/dotnet-sdk-latest-win-arm.zip.sha + +[win-arm64-badge-main]: https://aka.ms/dotnet/6.0/daily/win_arm64_Release_version_badge.svg +[win-arm64-version-main]: https://aka.ms/dotnet/6.0/daily/productCommit-win-arm64.txt +[win-arm64-installer-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-arm64.exe +[win-arm64-installer-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-arm64.exe.sha +[win-arm64-zip-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-arm64.zip +[win-arm64-zip-checksum-main]: https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-arm64.zip.sha + +[win-arm64-badge-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/win_arm64_Release_version_badge.svg +[win-arm64-version-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/productCommit-win-arm64.txt +[win-arm64-installer-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-arm64.exe +[win-arm64-installer-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-arm64.exe.sha +[win-arm64-zip-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-arm64.zip +[win-arm64-zip-checksum-5.0.1XX-rtm]: https://aka.ms/dotnet/net5/5.0.1xx/daily/Sdk/dotnet-sdk-win-arm64.zip.sha + +[win-arm64-badge-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/win_arm64_Release_version_badge.svg +[win-arm64-version-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/productCommit-win-arm64.txt +[win-arm64-installer-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-arm64.exe +[win-arm64-installer-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-arm64.exe.sha +[win-arm64-zip-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-arm64.zip +[win-arm64-zip-checksum-5.0.1XX-rc2]: https://aka.ms/dotnet/net5/rc2/Sdk/dotnet-sdk-win-arm64.zip.sha + +[sdk-shas-2.2.1XX]: https://github.com/dotnet/versions/tree/master/build-info/dotnet/product/cli/release/2.2#built-repositories""" diff --git a/tools/sdk-readme-table-generator/TableGenerator.Tests/TableGenerator.Tests.fsproj b/tools/sdk-readme-table-generator/TableGenerator.Tests/TableGenerator.Tests.fsproj new file mode 100644 index 000000000..2234374c1 --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator.Tests/TableGenerator.Tests.fsproj @@ -0,0 +1,17 @@ + + + net6.0 + + + + + + + + + + + + + + diff --git a/tools/sdk-readme-table-generator/TableGenerator.sln b/tools/sdk-readme-table-generator/TableGenerator.sln new file mode 100644 index 000000000..7e79f61ad --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29610.7 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TableGenerator", "TableGenerator\TableGenerator.fsproj", "{7F07B3CD-2E70-428F-8C7D-46A436211774}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TableGenerator.Tests", "TableGenerator.Tests\TableGenerator.Tests.fsproj", "{4957E781-3178-41A1-BC65-0A00F41B2685}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7F07B3CD-2E70-428F-8C7D-46A436211774}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F07B3CD-2E70-428F-8C7D-46A436211774}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F07B3CD-2E70-428F-8C7D-46A436211774}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F07B3CD-2E70-428F-8C7D-46A436211774}.Release|Any CPU.Build.0 = Release|Any CPU + {4957E781-3178-41A1-BC65-0A00F41B2685}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4957E781-3178-41A1-BC65-0A00F41B2685}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4957E781-3178-41A1-BC65-0A00F41B2685}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4957E781-3178-41A1-BC65-0A00F41B2685}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7B5BEADF-7D46-4729-A771-A306FBB2E154} + EndGlobalSection +EndGlobal diff --git a/tools/sdk-readme-table-generator/TableGenerator/Program.fs b/tools/sdk-readme-table-generator/TableGenerator/Program.fs new file mode 100644 index 000000000..bbbb7d4cf --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator/Program.fs @@ -0,0 +1,35 @@ +module TableGenerator.App + +open System +open TableGenerator.Shared +open TableGenerator.Reference +open TableGenerator.Table + +let inputBranches = + [ { GitBranchName = "main" + DisplayName = "main
(9.0.x Runtime)" + AkaMsChannel = Some("9.0.1xx/daily") } + { GitBranchName = "release/9.0.1xx-rc1" + DisplayName = "9.0.1xx-rc1
(9.0-rc1 Runtime)" + AkaMsChannel = Some("9.0.1xx-rc1/daily") } + { GitBranchName = "release/9.0.1xx" + DisplayName = "9.0.1xx
(9.0 Runtime)" + AkaMsChannel = Some("9.0.1xx/daily") }] + + +let referentNotes = """Reference notes: +> **1**: Our Debian packages are put together slightly differently than the other OS specific installers. Instead of combining everything, we have separate component packages that depend on each other. If you're installing the SDK from the .deb file (via dpkg or similar), then you'll need to install the corresponding dependencies first: +> * [Host, Host FX Resolver, and Shared Framework](https://github.com/dotnet/runtime/blob/main/docs/project/dogfooding.md#nightly-builds-table) +> * [ASP.NET Core Shared Framework](https://github.com/aspnet/AspNetCore/blob/main/docs/DailyBuilds.md)""" + +let wholeTable branches = + String.Join + (Environment.NewLine + Environment.NewLine, + [ table branches + referentNotes + referenceList branches ]) + +[] +let main argv = + Console.WriteLine(wholeTable inputBranches) + 0 diff --git a/tools/sdk-readme-table-generator/TableGenerator/Reference.fs b/tools/sdk-readme-table-generator/TableGenerator/Reference.fs new file mode 100644 index 000000000..c6e8cabeb --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator/Reference.fs @@ -0,0 +1,263 @@ +module TableGenerator.Reference + +open System +open TableGenerator.Shared + +let referenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[{0}-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[{0}-installer-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.exe +[{0}-installer-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.exe.sha +[{0}-zip-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.zip +[{0}-zip-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.zip.sha""" + + AkaMSLegacyTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[{0}-installer-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.exe +[{0}-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.exe.sha +[{0}-zip-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.zip +[{0}-zip-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.zip.sha""" + + AkaMSTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[{0}-installer-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.exe +[{0}-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.exe.sha +[{0}-zip-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.zip +[{0}-zip-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.zip.sha""" +} + +let targzReferenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[{0}-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[{0}-targz-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz.sha""" + + AkaMSLegacyTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz.sha""" + + AkaMSTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz.sha""" } + +let linuxArmNoArchitectureReferenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[{0}-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[{0}-targz-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz.sha""" + + AkaMSLegacyTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz.sha""" + + AkaMSTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz.sha""" } + +let osxReferenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[{0}-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[{0}-installer-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.pkg +[{0}-installer-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.pkg.sha +[{0}-targz-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz.sha""" + + AkaMSLegacyTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[{0}-installer-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.pkg +[{0}-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.pkg.sha +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.pkg.tar.gz.sha""" + + AkaMSTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[{0}-installer-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.pkg +[{0}-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.pkg.sha +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.pkg.tar.gz.sha""" +} + +let linuxReferenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[linux-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[linux-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[linux-DEB-installer-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-x64.deb +[linux-DEB-installer-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-x64.deb.sha +[linux-RPM-installer-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-x64.rpm +[linux-RPM-installer-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-x64.rpm.sha +[linux-targz-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz +[linux-targz-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz.sha""" + + AkaMSLegacyTemplate = """[linux-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[linux-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[linux-DEB-installer-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-x64.deb +[linux-DEB-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-x64.deb.sha +[linux-RPM-installer-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-x64.rpm +[linux-RPM-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-x64.rpm.sha +[linux-targz-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz +[linux-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz.sha""" + + AkaMSTemplate = """[linux-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[linux-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[linux-DEB-installer-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-x64.deb +[linux-DEB-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-x64.deb.sha +[linux-RPM-installer-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-x64.rpm +[linux-RPM-installer-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-x64.rpm.sha +[linux-targz-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz +[linux-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz.sha""" +} + +let rhel6ReferenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[rhel-6-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[rhel-6-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[rhel-6-targz-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz +[rhel-6-targz-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz.sha""" + + AkaMSLegacyTemplate = """[rhel-6-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[rhel-6-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[rhel-6-targz-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz +[rhel-6-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz.sha""" + + AkaMSTemplate = """[rhel-6-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[rhel-6-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[rhel-6-targz-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz +[rhel-6-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz.sha""" +} + +let linuxMuslReferenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[{0}-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[{0}-targz-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.tar.gz.sha""" + + AkaMSLegacyTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.tar.gz.sha""" + + AkaMSTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[{0}-targz-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz +[{0}-targz-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.tar.gz.sha""" +} + +let winMuslReferenceTemplate: ReferenceTemplate = { + LegacyTemplate = """[{0}-badge-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/latest.version +[{0}-zip-{1}]: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.zip +[{0}-zip-checksum-{1}]: https://dotnetclichecksums.blob.core.windows.net/dotnet/Sdk/{2}/dotnet-sdk-latest-{0}.zip.sha""" + + AkaMSLegacyTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/Sdk/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/Sdk/productCommit-{0}.txt +[{0}-zip-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.zip +[{0}-zip-checksum-{1}]: https://aka.ms/dotnet/{4}/Sdk/dotnet-sdk-{0}.zip.sha""" + + AkaMSTemplate = """[{0}-badge-{1}]: https://aka.ms/dotnet/{4}/{3}_Release_version_badge.svg?no-cache +[{0}-version-{1}]: https://aka.ms/dotnet/{4}/productCommit-{0}.txt +[{0}-zip-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.zip +[{0}-zip-checksum-{1}]: https://aka.ms/dotnet/{4}/dotnet-sdk-{0}.zip.sha""" +} + +let formatTemplate (platform: String) (template: ReferenceTemplate) (branch: Branch): Option = + if branch.AkaMsChannel <> None then + let useLegacyAkaMsTemplate = + match getMajorMinor branch with + | Main -> false + | MajorMinor { Major = major; Minor = _minor; Patch = patch } when major = 5 && patch < 300 -> true + | _ -> false + if useLegacyAkaMsTemplate then + Some (String.Format(template.AkaMSLegacyTemplate, + platform, // 0 - win-x64 + (branchNameShorten branch), // 1 - 5.0.1xx-preview2 + branch.GitBranchName, // 2 - 5.0.100-preview.2.20169.1 + (platform.Replace('-', '_')), // 3 - win_64 + branch.AkaMsChannel.Value)) // 4 - 5.0/preview2 + else + Some (String.Format(template.AkaMSTemplate, + platform, // 0 - win-x64 + (branchNameShorten branch), // 1 - 5.0.1xx-preview2 + branch.GitBranchName, // 2 - 5.0.100-preview.2.20169.1 + (platform.Replace('-', '_')), // 3 - win_64 + branch.AkaMsChannel.Value)) // 4 - 5.0/preview2 + else + Some (String.Format(template.LegacyTemplate, + platform, // 0 - win-64 + (branchNameShorten branch), // 1 - 5.0.1xx-preview2 + branch.GitBranchName, // 2 - 5.0.100-preview.2.20169.1 + (platform.Replace('-', '_')))) // 3 - win_64 + +let winX64ReferenceTemplate = formatTemplate "win-x64" referenceTemplate + +let winX86ReferenceTemplate = formatTemplate "win-x86" referenceTemplate + +let osxX64ReferenceTemplate = formatTemplate "osx-x64" osxReferenceTemplate + +let osxArm64ReferenceTemplate branch = + let format branch = formatTemplate "osx-arm64" osxReferenceTemplate branch + match getMajorMinor branch with + | Main -> format branch + | MajorMinor { Major = major; Minor = _minor } when major >= 6 -> format branch + | _ -> None + +let linuxX64ReferenceTemplate = formatTemplate "linux-x64" linuxReferenceTemplate + +let linuxArmReferenceTemplate = formatTemplate "linux-arm" linuxArmNoArchitectureReferenceTemplate + +let linuxArm64ReferenceTemplate = formatTemplate "linux-arm64" linuxArmNoArchitectureReferenceTemplate + +let rhel6x64ReferenceTemplate = formatTemplate "rhel.6-x64" rhel6ReferenceTemplate + +let linuxMuslx64ReferenceTemplate = formatTemplate "linux-musl-x64" linuxMuslReferenceTemplate + +let linuxMuslArmReferenceTemplate branch = + let format branch = formatTemplate "linux-musl-arm" linuxMuslReferenceTemplate branch + match getMajorMinor branch with + | Main -> format branch + | MajorMinor { Major = major; Minor = _minor; Patch = patch } when major >= 6 || (major >= 5 && patch >= 299) -> format branch + | _ -> None + +let linuxMuslArm64ReferenceTemplate branch = + let format branch = formatTemplate "linux-musl-arm64" linuxMuslReferenceTemplate branch + match getMajorMinor branch with + | Main -> format branch + | MajorMinor { Major = major; Minor = _minor; Patch = patch } when major >= 6 || (major >= 5 && patch >= 299) -> format branch + | _ -> None + +let winArmMuslReferenceTemplate branch = + match getMajorMinor branch with + | NoVersion -> None + | MajorMinor { Major = major; Minor = minor } when major <= 2 && minor <= 1 -> None + | _ -> formatTemplate "win-arm" winMuslReferenceTemplate branch + +let winArm64MuslReferenceTemplate branch = + match getMajorMinor branch with + | NoVersion -> None + | MajorMinor { Major = major; Minor = minor } when major < 5 -> None + | _ -> formatTemplate "win-arm64" referenceTemplate branch + +let templates = + [ winX64ReferenceTemplate + winX86ReferenceTemplate + osxX64ReferenceTemplate + osxArm64ReferenceTemplate + linuxX64ReferenceTemplate + linuxArmReferenceTemplate + linuxArm64ReferenceTemplate + rhel6x64ReferenceTemplate + linuxMuslx64ReferenceTemplate + linuxMuslArmReferenceTemplate + linuxMuslArm64ReferenceTemplate + winArmMuslReferenceTemplate + winArm64MuslReferenceTemplate ] + +let referenceList branches = + String.Join + (Environment.NewLine + Environment.NewLine, + templates + |> List.collect (fun template -> + branches + |> List.map template + |> List.choose id)) diff --git a/tools/sdk-readme-table-generator/TableGenerator/Shared.fs b/tools/sdk-readme-table-generator/TableGenerator/Shared.fs new file mode 100644 index 000000000..13c133b11 --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator/Shared.fs @@ -0,0 +1,45 @@ +module TableGenerator.Shared + +open NuGet.Versioning +open System + +type Branch = + { GitBranchName: string + DisplayName: string + AkaMsChannel: string option } + +type ReferenceTemplate = + { AkaMSTemplate: string + LegacyTemplate: string + AkaMSLegacyTemplate: string} + +let branchNameShorten (branch: Branch): string = + branch.GitBranchName.Substring(branch.GitBranchName.IndexOf('/') + 1).Replace("xx", "XX") + +type BranchMajorMinorVersion = + { Major: int + Minor: int + Patch: int + Release: string} + +type BranchMajorMinorVersionOrmain = + | Main + | MajorMinor of BranchMajorMinorVersion + | NoVersion + +let getMajorMinor (branch: Branch): BranchMajorMinorVersionOrmain = + match branch.GitBranchName = "main" with + | true -> Main + | _ -> + match branch.GitBranchName.IndexOf('/') with + | index when index < 0 -> NoVersion + | _ -> + match NuGetVersion.TryParseStrict + (branch.GitBranchName.Substring(branch.GitBranchName.IndexOf('/') + 1).Replace("xx", "99")) with + | true, version -> + MajorMinor + { Major = version.Major + Minor = version.Minor + Patch = version.Patch + Release = version.Release} + | _ -> NoVersion diff --git a/tools/sdk-readme-table-generator/TableGenerator/Table.fs b/tools/sdk-readme-table-generator/TableGenerator/Table.fs new file mode 100644 index 000000000..ec95080ba --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator/Table.fs @@ -0,0 +1,141 @@ +module TableGenerator.Table + +open System +open TableGenerator.Shared + +let notAvailable = "**N/A**" + +let windowsDesktopArchTableTemplate = + """[![][{0}-badge-{1}]][{0}-version-{1}]
[Installer][{0}-installer-{1}] - [Checksum][{0}-installer-checksum-{1}]
[zip][{0}-zip-{1}] - [Checksum][{0}-zip-checksum-{1}]""" + +let linuxArmTableTemplate = + "[![][{0}-badge-{1}]][{0}-version-{1}]
[tar.gz][{0}-targz-{1}] - [Checksum][{0}-targz-checksum-{1}]" + +let osxDesktopArchTableTemplate = + """[![][{0}-badge-{1}]][{0}-version-{1}]
[Installer][{0}-installer-{1}] - [Checksum][{0}-installer-checksum-{1}]
[tar.gz][{0}-targz-{1}] - [Checksum][{0}-targz-checksum-{1}]""" + +let joinListOfArchs (listOfArchs: List): string = "| " + String.Join(" | ", listOfArchs) + " |" + +let formRow rowTitle tableTemplateForThisArch branches = + let inList = + List.concat + [ [ rowTitle ] + (branches |> List.map tableTemplateForThisArch) ] + joinListOfArchs inList + +let windowsX64Row branches = + let tableTemplateForThisArch branch = + String.Format(windowsDesktopArchTableTemplate, "win-x64", branchNameShorten branch) + formRow "**Windows x64**" tableTemplateForThisArch branches + +let windowsX86Row branches = + let tableTemplateForThisArch branch = + String.Format(windowsDesktopArchTableTemplate, "win-x86", branchNameShorten branch) + formRow "**Windows x86**" tableTemplateForThisArch branches + +let osxX64Row branches = + let tableTemplateForThisArch branch = String.Format(osxDesktopArchTableTemplate, "osx-x64", branchNameShorten branch) + formRow "**macOS x64**" tableTemplateForThisArch branches + +let osxArm64Row branches = + let format branch = String.Format(osxDesktopArchTableTemplate, "osx-arm64", branchNameShorten branch) + let tableTemplateForThisArch branch = + match getMajorMinor branch with + | Main -> format branch + | MajorMinor { Major = major; Minor = _minor } when major >= 6 -> format branch + | _ -> notAvailable + formRow "**macOS arm64**" tableTemplateForThisArch branches + +let linuxDesktopArchRow branches = + let tableTemplate = + """[![][linux-badge-{0}]][linux-version-{0}]
[DEB Installer][linux-DEB-installer-{0}] - [Checksum][linux-DEB-installer-checksum-{0}]
[RPM Installer][linux-RPM-installer-{0}] - [Checksum][linux-RPM-installer-checksum-{0}]
_see installer note below_1
[tar.gz][linux-targz-{0}] - [Checksum][linux-targz-checksum-{0}]""" + let tableTemplateForThisArch branch = String.Format(tableTemplate, branchNameShorten branch) + formRow "**Linux x64**" tableTemplateForThisArch branches + +let linuxArmRow branches = + let tableTemplateForThisArch branch = String.Format(linuxArmTableTemplate, "linux-arm", branchNameShorten branch) + formRow "**Linux arm**" tableTemplateForThisArch branches + +let linuxArm64Row branches = + let tableTemplateForThisArch branch = String.Format(linuxArmTableTemplate, "linux-arm64", branchNameShorten branch) + formRow "**Linux arm64**" tableTemplateForThisArch branches + +let rhel6Row branches = + let tableTemplateForThisArch branch = + match getMajorMinor branch with + | NoVersion -> notAvailable + | Main -> notAvailable + | MajorMinor { Major = major; Minor = minor } when major >= 5 -> notAvailable + | _ -> String.Format(linuxArmTableTemplate, "rhel-6", branchNameShorten branch) + formRow "**RHEL 6**" tableTemplateForThisArch branches + +let linuxMuslRowX64 branches = + let format branch = String.Format(linuxArmTableTemplate, "linux-musl-x64", branchNameShorten branch) + let tableTemplateForThisArch branch = format branch + formRow "**Linux-musl-x64**" tableTemplateForThisArch branches + +let linuxMuslRowArm branches = + let format branch = String.Format(linuxArmTableTemplate, "linux-musl-arm", branchNameShorten branch) + let tableTemplateForThisArch branch = + match getMajorMinor branch with + | Main -> format branch + | MajorMinor { Major = major; Minor = _minor; Patch = patch } when major >= 6 || (major >= 5 && patch >= 299) -> format branch + | _ -> notAvailable + formRow "**Linux-musl-arm**" tableTemplateForThisArch branches + +let linuxMuslRowArm64 branches = + let format branch = String.Format(linuxArmTableTemplate, "linux-musl-arm64", branchNameShorten branch) + let tableTemplateForThisArch branch = + match getMajorMinor branch with + | Main -> format branch + | MajorMinor { Major = major; Minor = _minor; Patch = patch } when major >= 6 || (major >= 5 && patch >= 299) -> format branch + | _ -> notAvailable + formRow "**Linux-musl-arm64**" tableTemplateForThisArch branches + +let windowsArmRow branches = + let tableTemplate = + "[![][win-arm-badge-{0}]][win-arm-version-{0}]
[zip][win-arm-zip-{0}] - [Checksum][win-arm-zip-checksum-{0}]" + + let tableTemplateForThisArch branch = + match getMajorMinor branch with + | NoVersion -> notAvailable + | Main -> notAvailable + | MajorMinor { Major = major; Minor = minor } when ( major >= 5) -> notAvailable + | _ -> String.Format(tableTemplate, branchNameShorten branch) + formRow "**Windows arm**" tableTemplateForThisArch branches + +let windowsArm64Row branches = + let tableTemplate = + """[![][win-arm64-badge-{0}]][win-arm64-version-{0}]
[Installer][win-arm64-installer-{0}] - [Checksum][win-arm64-installer-checksum-{0}]
[zip][win-arm64-zip-{0}]""" + + let tableTemplateForThisArch branch = + match getMajorMinor branch with + | NoVersion -> notAvailable + | MajorMinor { Major = major; Minor = minor; Release = release; } when major <= 3 -> notAvailable + | MajorMinor { Major = major; Minor = minor; Release = release; } when major = 5 -> String.Format(tableTemplate, branchNameShorten branch) + | _ -> String.Format(tableTemplate, branchNameShorten branch) + formRow "**Windows arm64**" tableTemplateForThisArch branches + +let titleRow = formRow "Platform" (fun (b: Branch) -> b.DisplayName) + +let separator = formRow ":---------" (fun _ -> ":----------:") + +let rows = + [ titleRow + separator + windowsX64Row + windowsX86Row + windowsArmRow + windowsArm64Row + osxX64Row + osxArm64Row + linuxDesktopArchRow + linuxArmRow + linuxArm64Row + linuxMuslRowX64 + linuxMuslRowArm + linuxMuslRowArm64 + rhel6Row + ] + +let table branches = String.Join(Environment.NewLine, rows |> List.map (fun row -> row branches)) diff --git a/tools/sdk-readme-table-generator/TableGenerator/TableGenerator.fsproj b/tools/sdk-readme-table-generator/TableGenerator/TableGenerator.fsproj new file mode 100644 index 000000000..6564d2f11 --- /dev/null +++ b/tools/sdk-readme-table-generator/TableGenerator/TableGenerator.fsproj @@ -0,0 +1,19 @@ + + + + Exe + net6.0 + + + + + + + + + + + + + + diff --git a/tools/sdk-readme-table-generator/old.config b/tools/sdk-readme-table-generator/old.config new file mode 100644 index 000000000..ca3422d76 --- /dev/null +++ b/tools/sdk-readme-table-generator/old.config @@ -0,0 +1,9 @@ + + + + + + + + +