diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 25687b74..5b69a7c2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -91,6 +91,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: dotnet fsi scripts/runUnitTests.fsx @@ -118,6 +120,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: fsharpi --define:LEGACY_FRAMEWORK scripts/runUnitTests.fsx @@ -149,6 +153,8 @@ jobs: run: ./configure.sh && cat build.config - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: dotnet fsi scripts/runUnitTests.fsx @@ -180,6 +186,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: dotnet fsi scripts/runUnitTests.fsx @@ -222,6 +230,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: install run: | @@ -266,6 +276,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: fsharpi --define:LEGACY_FRAMEWORK scripts/runUnitTests.fsx @@ -297,6 +309,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: dotnet fsi scripts/runUnitTests.fsx @@ -328,6 +342,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: dotnet fsi scripts/runUnitTests.fsx @@ -370,6 +386,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: install run: | @@ -414,6 +432,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: fsharpi --define:LEGACY_FRAMEWORK scripts/runUnitTests.fsx @@ -452,6 +472,8 @@ jobs: run: ./configure.sh - name: build in DEBUG mode run: make + - name: sanity check + run: dotnet fsi scripts/sanitycheck.fsx - name: run unit tests run: dotnet fsi scripts/runUnitTests.fsx @@ -477,6 +499,8 @@ jobs: run: del $(where.exe dotnet) - name: build in DEBUG mode run: .\make.bat + - name: sanity check + run: .\make.bat sanitycheck - name: run unit tests run: .\Tools\fsi.bat scripts\runUnitTests.fsx @@ -503,6 +527,8 @@ jobs: dotnet-version: '6.0.x' - name: build in DEBUG mode run: .\make.bat + - name: sanity check + run: .\make.bat sanitycheck - name: run unit tests run: dotnet fsi scripts/runUnitTests.fsx diff --git a/Makefile b/Makefile index 863b5057..ad46e498 100644 --- a/Makefile +++ b/Makefile @@ -13,3 +13,6 @@ reinstall: check: ./scripts/runTests.fsx + +sanitycheck: + @./scripts/build.sh sanitycheck \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh index f9bfb9a9..cb6127a1 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,16 +1,9 @@ #!/usr/bin/env bash -set -e +set -exo pipefail -if [ ! -f ./build.config ]; then - echo "Please run ./configure.sh first" >&2 - exit 1 +BUILD_CONFIG="./build.config" +if [ ! -f "$BUILD_CONFIG" ]; then + echo "ERROR: configure hasn't been run yet, run ./configure.sh first" >&2 && exit 1 fi -source build.config - -if [[ ! $BuildTool == dotnet* ]]; then - mkdir -p .nuget/ - curl -o .nuget/NuGet.exe https://dist.nuget.org/win-x86-commandline/v5.4.0/nuget.exe - mono .nuget/NuGet.exe restore $Solution -fi - -$BuildTool $Solution $1 +source "$BUILD_CONFIG" +FsxRunnerBin=$FsxRunnerBin FsxRunnerArg=$FsxRunnerArg BuildTool=$BuildTool LegacyBuildTool=$LegacyBuildTool $FsxRunnerBin $FsxRunnerArg ./scripts/make.fsx "$@" diff --git a/scripts/fsxHelper.fs b/scripts/fsxHelper.fs new file mode 100644 index 00000000..74664b74 --- /dev/null +++ b/scripts/fsxHelper.fs @@ -0,0 +1,54 @@ +namespace GWallet.Scripting + +open System +open System.IO + +open Fsdk +open Fsdk.Process + +module FsxHelper = + + let ScriptsDir = __SOURCE_DIRECTORY__ |> DirectoryInfo + let RootDir = Path.Combine(ScriptsDir.FullName, "..") |> DirectoryInfo + let SourceDir = Path.Combine(ScriptsDir.FullName, "../src/") |> DirectoryInfo + let NugetDir = Path.Combine (RootDir.FullName, ".nuget") |> DirectoryInfo + let NugetExe = Path.Combine (NugetDir.FullName, "nuget.exe") |> FileInfo + let NugetSolutionPackagesDir = Path.Combine(RootDir.FullName, "packages") |> DirectoryInfo + let NugetScriptsPackagesDir() = + let dir = Path.Combine(NugetDir.FullName, "packages") |> DirectoryInfo + if not dir.Exists then + Directory.CreateDirectory dir.FullName + |> ignore + dir + + let AreGtkLibsPresent echoMode = + if Misc.GuessPlatform() <> Misc.Platform.Linux then + failwith "Gtk is only supported in Linux" + + let pkgConfigForGtkProc = Process.Execute({ Command = "pkg-config"; Arguments = "gtk-sharp-2.0" }, echoMode) + match pkgConfigForGtkProc.Result with + | Error _ -> false + | _ -> true + + let FsxRunnerInfo() = + match Misc.GuessPlatform() with + | Misc.Platform.Windows -> +#if !LEGACY_FRAMEWORK + "dotnet", "fsi" +#else + Path.Combine(ScriptsDir.FullName, "fsx", "Tools", "fsi.bat"), String.Empty +#endif + | _ -> + let fsxRunnerBinEnvVar = Environment.GetEnvironmentVariable "FsxRunnerBin" + let fsxRunnerArgEnvVar = Environment.GetEnvironmentVariable "FsxRunnerArg" + if String.IsNullOrEmpty fsxRunnerBinEnvVar then + let msg = "FsxRunnerBin env var not found, it should have been sourced from build.config file" + let msgFull = + msg + Environment.NewLine + + (sprintf "(maybe you 1. %s, or 2. %s, or 3. %s)" + "called this from configure.fsx (which is not supported, just use this func from make.fsx or sanitycheck.fsx)" + "you meant to run a Makefile target rather than this script directly?" + "there is a .sh wrapper script for your .fsx script" + ) + failwith msgFull + fsxRunnerBinEnvVar, fsxRunnerArgEnvVar diff --git a/scripts/make.fsx b/scripts/make.fsx old mode 100644 new mode 100755 index aea0c10f..0c59c906 --- a/scripts/make.fsx +++ b/scripts/make.fsx @@ -362,6 +362,34 @@ match maybeTarget with | "0" -> () | _ -> failwith "Unexpected output from tests (error propagation)" +| Some "sanitycheck" -> + +#if LEGACY_FRAMEWORK + if not FsxHelper.NugetExe.Exists then + MakeAll None |> ignore + + let microsoftBuildLibVersion = "16.11.0" + let pkgOutputDir = FsxHelper.NugetScriptsPackagesDir() + Network.InstallNugetPackage + FsxHelper.NugetExe + pkgOutputDir + "Microsoft.Build" + (Some microsoftBuildLibVersion) + Echo.All + |> ignore + +#endif + + let sanityCheckScript = Path.Combine(FsxHelper.ScriptsDir.FullName, "sanitycheck.fsx") + let fsxRunnerBin,fsxRunnerArg = FsxHelper.FsxRunnerInfo() + Process.Execute( + { + Command = fsxRunnerBin + Arguments = sprintf "%s %s" fsxRunnerArg sanityCheckScript + }, + Echo.All + ).UnwrapDefault() |> ignore + | Some someOtherTarget -> Console.Error.WriteLine("Unrecognized target: " + someOtherTarget) Environment.Exit 1 diff --git a/scripts/sanitycheck.fsx b/scripts/sanitycheck.fsx new file mode 100644 index 00000000..afaf61e4 --- /dev/null +++ b/scripts/sanitycheck.fsx @@ -0,0 +1,464 @@ +#!/usr/bin/env -S dotnet fsi + +open System +open System.IO +open System.Linq +open System.Diagnostics + +open System.Text +open System.Text.RegularExpressions +#r "System.Core.dll" +open System.Xml +#r "System.Xml.Linq.dll" +open System.Xml.Linq +open System.Xml.XPath + +#if !LEGACY_FRAMEWORK +#r "nuget: Fsdk, Version=0.6.0--date20230812-0646.git-2268d50" +#else +#r "System.Configuration" +open System.Configuration +#load "fsx/Fsdk/Misc.fs" +#load "fsx/Fsdk/Process.fs" +#load "fsx/Fsdk/Git.fs" +#endif +open Fsdk +open Fsdk.Process + +#load "fsxHelper.fs" +open GWallet.Scripting + +#if LEGACY_FRAMEWORK +#r "../.nuget/packages/Microsoft.Build.16.11.0/lib/net472/Microsoft.Build.dll" +#else +#r "nuget: Microsoft.Build, Version=16.11.0" +#endif +open Microsoft.Build.Construction + + +module MapHelper = + let GetKeysOfMap (map: Map<'K,'V>): seq<'K> = + map |> Map.toSeq |> Seq.map fst + + let MergeIntoMap<'K,'V when 'K: comparison> (from: seq<'K*'V>): Map<'K,seq<'V>> = + let keys = from.Select (fun (k, v) -> k) + let keyValuePairs = + seq { + for key in keys do + let valsForKey = (from.Where (fun (k, v) -> key = k)).Select (fun (k, v) -> v) |> seq + yield key,valsForKey + } + keyValuePairs |> Map.ofSeq + +[] +type private PackageInfo = + { + PackageId: string + PackageVersion: string + ReqReinstall: Option + } + +type private DependencyHolder = + { Name: string } + +[] +type private ComparableFileInfo = + { + File: FileInfo + } + member self.DependencyHolderName: DependencyHolder = + if self.File.FullName.ToLower().EndsWith ".nuspec" then + { Name = self.File.Name } + else + { Name = self.File.Directory.Name + "/" } + + interface IComparable with + member this.CompareTo obj = + match obj with + | null -> this.File.FullName.CompareTo null + | :? ComparableFileInfo as other -> this.File.FullName.CompareTo other.File.FullName + | _ -> invalidArg "obj" "not a ComparableFileInfo" + override this.Equals obj = + match obj with + | :? ComparableFileInfo as other -> + this.File.FullName.Equals other.File.FullName + | _ -> false + override this.GetHashCode () = + this.File.FullName.GetHashCode () + +let FindOffendingPrintfUsage () = + let findScript = Path.Combine (FsxHelper.RootDir.FullName, "scripts", "find.fsx") + let excludeFolders = + String.Format ( + "scripts{0}" + + "src{1}GWallet.Frontend.Console{0}" + + "src{1}GWallet.Backend.Tests{0}" + + "src{1}GWallet.Backend{1}FSharpUtil.fs", + Path.PathSeparator, + Path.DirectorySeparatorChar + ) + + let fsxRunnerBin, fsxRunnerArg = FsxHelper.FsxRunnerInfo() + let proc = + { + Command = fsxRunnerBin + Arguments = sprintf "%s %s --exclude=%s %s" + fsxRunnerArg + findScript + excludeFolders + "printf failwithf" + } + let findProcOutput = Process.Execute(proc, Echo.All).UnwrapDefault() + if findProcOutput.Trim().Length > 0 then + Console.Error.WriteLine "Illegal usage of printf/printfn/sprintf/sprintfn/failwithf detected; use SPrintF1/SPrintF2/... instead" + Environment.Exit 1 + + +let SanityCheckNugetPackages () = + + let notPackagesFolder (dir: DirectoryInfo): bool = + dir.FullName <> FsxHelper.NugetSolutionPackagesDir.FullName + + let notSubmodule (dir: DirectoryInfo): bool = + let getSubmoduleDirsForThisRepo (): seq = + let regex = Regex("path\s*=\s*([^\s]+)") + seq { + for regexMatch in regex.Matches (File.ReadAllText (".gitmodules")) do + let submoduleFolderRelativePath = regexMatch.Groups.[1].ToString () + let submoduleFolder = + DirectoryInfo ( + Path.Combine (Directory.GetCurrentDirectory (), submoduleFolderRelativePath) + ) + yield submoduleFolder + } + not (getSubmoduleDirsForThisRepo().Any (fun d -> dir.FullName = d.FullName)) + + // this seems to be a bug in Microsoft.Build nuget library, FIXME: report + let normalizeDirSeparatorsPaths (path: string): string = + path + .Replace('\\', Path.DirectorySeparatorChar) + .Replace('/', Path.DirectorySeparatorChar) + + let sanityCheckNugetPackagesFromSolution (sol: FileInfo) = +#if !LEGACY_FRAMEWORK + let rec findProjectFiles (): seq = + let parsedSolution = SolutionFile.Parse sol.FullName + seq { + for projPath in (parsedSolution.ProjectsInOrder.Select(fun proj -> normalizeDirSeparatorsPaths proj.AbsolutePath).ToList()) do + if projPath.ToLower().EndsWith ".fsproj" || projPath.ToLower().EndsWith ".csproj" then + yield (FileInfo projPath) + } +#else + let findPackagesDotConfigFiles (): seq = + let parsedSolution = SolutionFile.Parse sol.FullName + seq { + for projPath in (parsedSolution.ProjectsInOrder.Select(fun proj -> normalizeDirSeparatorsPaths proj.AbsolutePath).ToList()) do + if projPath.ToLower().EndsWith ".fsproj" then + for file in ((FileInfo projPath).Directory).EnumerateFiles () do + if file.Name.ToLower () = "packages.config" then + yield file + } +#endif + + let rec findNuspecFiles (dir: DirectoryInfo): seq = + dir.Refresh () + seq { + for file in dir.EnumerateFiles () do + if (file.Name.ToLower ()).EndsWith ".nuspec" then + yield file + for subdir in dir.EnumerateDirectories().Where(notSubmodule).Where(notPackagesFolder) do + for file in findNuspecFiles subdir do + yield file + } + + let getPackageTree (sol: FileInfo): Map> = +#if !LEGACY_FRAMEWORK + let projectFiles = findProjectFiles() + let projectElements = + seq { + for projectFile in projectFiles do + let xmlDoc = XDocument.Load projectFile.FullName + let query = "//PackageReference" + let pkgReferences = xmlDoc.XPathSelectElements query + + for pkgReference in pkgReferences do + let id = pkgReference.Attributes().Single(fun attr -> attr.Name.LocalName = "Include" || attr.Name.LocalName = "Update").Value + let version = pkgReference.Attributes().Single(fun attr -> attr.Name.LocalName = "Version").Value + yield { File = projectFile }, { PackageId = id; PackageVersion = version; ReqReinstall = None } + } |> List.ofSeq +#else + let packagesConfigFiles = findPackagesDotConfigFiles() + let projectElements = + seq { + for packagesConfigFile in packagesConfigFiles do + let xmlDoc = XDocument.Load packagesConfigFile.FullName + for descendant in xmlDoc.Descendants () do + if descendant.Name.LocalName.ToLower() = "package" then + let id = descendant.Attributes().Single(fun attr -> attr.Name.LocalName = "id").Value + let version = descendant.Attributes().Single(fun attr -> attr.Name.LocalName = "version").Value + let reqReinstall = descendant.Attributes().Any(fun attr -> attr.Name.LocalName = "requireReinstallation") + yield { File = packagesConfigFile }, { PackageId = id; PackageVersion = version; ReqReinstall = Some reqReinstall } + } |> List.ofSeq +#endif + + let solDir = sol.Directory + solDir.Refresh () + let nuspecFiles = findNuspecFiles solDir + let nuspecFileElements = + seq { + for nuspecFile in nuspecFiles do + let xmlDoc = XDocument.Load nuspecFile.FullName + + let nsOpt = + let nsString = xmlDoc.Root.Name.Namespace.ToString() + if String.IsNullOrEmpty nsString then + None + else + let nsManager = XmlNamespaceManager(NameTable()) + let nsPrefix = "x" + nsManager.AddNamespace(nsPrefix, nsString) + if nsString <> "http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd" then + Console.Error.WriteLine "Warning: the namespace URL doesn't match expectations, nuspec's XPath query may result in no elements" + Some(nsManager, sprintf "%s:" nsPrefix) + let query = "//{0}dependency" + let dependencies = + match nsOpt with + | None -> + let fixedQuery = String.Format(query, String.Empty) + xmlDoc.XPathSelectElements fixedQuery + | Some (nsManager, nsPrefix) -> + let fixedQuery = String.Format(query, nsPrefix) + xmlDoc.XPathSelectElements(fixedQuery, nsManager) + + for dependency in dependencies do + let id = dependency.Attributes().Single(fun attr -> attr.Name.LocalName = "id").Value + let version = dependency.Attributes().Single(fun attr -> attr.Name.LocalName = "version").Value + yield { File = nuspecFile }, { PackageId = id; PackageVersion = version; ReqReinstall = None } + } |> List.ofSeq + + let allElements = Seq.append projectElements nuspecFileElements + + allElements + |> MapHelper.MergeIntoMap + + let getAllPackageIdsAndVersions (packageTree: Map>): Map> = + seq { + for KeyValue (dependencyHolderFile, pkgs) in packageTree do + for pkg in pkgs do + yield pkg, dependencyHolderFile.DependencyHolderName + } |> MapHelper.MergeIntoMap + + let getDirectoryNamesForPackagesSet (packages: Map>): Map> = + seq { + for KeyValue (package, prjs) in packages do +#if !LEGACY_FRAMEWORK + let dirForPackage = + sprintf "%s%s%s" + (package.PackageId.ToLower()) + (Path.DirectorySeparatorChar.ToString()) + package.PackageVersion +#else + let dirForPackage = sprintf "%s.%s" package.PackageId package.PackageVersion +#endif + yield dirForPackage, prjs + } |> Map.ofSeq + + let findMissingPackageDirs (solDir: DirectoryInfo) (idealPackageDirs: Map>): Map> = + solDir.Refresh () + if not FsxHelper.NugetSolutionPackagesDir.Exists then + failwithf "'%s' subdir under solution dir %s doesn't exist, run `make` first" + FsxHelper.NugetSolutionPackagesDir.Name + FsxHelper.NugetSolutionPackagesDir.FullName + let packageDirsAbsolutePaths = FsxHelper.NugetSolutionPackagesDir.EnumerateDirectories().Select (fun dir -> dir.FullName) + if not (packageDirsAbsolutePaths.Any()) then + Console.Error.WriteLine ( + sprintf "'%s' subdir under solution dir %s doesn't contain any packages" + FsxHelper.NugetSolutionPackagesDir.Name + FsxHelper.NugetSolutionPackagesDir.FullName + ) + Console.Error.WriteLine "Maybe you forgot to issue the commands `git submodule sync --recursive && git submodule update --init --recursive`?" + Environment.Exit 1 + + seq { + for KeyValue (packageDirNameThatShouldExist, prjs) in idealPackageDirs do + let pkgDirToLookFor = + Path.Combine(FsxHelper.NugetSolutionPackagesDir.FullName, packageDirNameThatShouldExist) + |> DirectoryInfo + if not pkgDirToLookFor.Exists then + yield packageDirNameThatShouldExist, prjs + } |> Map.ofSeq + + let findExcessPackageDirs (solDir: DirectoryInfo) (idealPackageDirs: Map>): seq = + solDir.Refresh () + if not (FsxHelper.NugetSolutionPackagesDir.Exists) then + failwithf "'%s' subdir under solution dir %s doesn't exist, run `make` first" + FsxHelper.NugetSolutionPackagesDir.Name + FsxHelper.NugetSolutionPackagesDir.FullName + // "src" is a directory for source codes and build scripts, + // not for packages, so we need to exclude it from here + let packageDirNames = FsxHelper.NugetSolutionPackagesDir.EnumerateDirectories().Select(fun dir -> dir.Name).Except(["src"]) + if not (packageDirNames.Any()) then + failwithf "'%s' subdir under solution dir %s doesn't contain any packages" + FsxHelper.NugetSolutionPackagesDir.Name + FsxHelper.NugetSolutionPackagesDir.FullName + let packageDirsThatShouldExist = MapHelper.GetKeysOfMap idealPackageDirs + seq { + for packageDirThatExists in packageDirNames do + if not (packageDirsThatShouldExist.Contains packageDirThatExists) then + yield packageDirThatExists + } + + let findPackagesWithMoreThanOneVersion + (packageTree: Map>) + : Map> = + + let getAllPackageInfos (packages: Map>) = + let pkgInfos = + seq { + for KeyValue (_, pkgs) in packages do + for pkg in pkgs do + yield pkg + } + Set pkgInfos + + let getAllPackageVersionsForPackageId (packages: seq) (packageId: string) = + seq { + for package in packages do + if package.PackageId = packageId then + yield package.PackageVersion + } |> Set + + let packageInfos = getAllPackageInfos packageTree + let packageIdsWithMoreThan1Version = + seq { + for packageId in packageInfos.Select (fun pkg -> pkg.PackageId) do + let versions = getAllPackageVersionsForPackageId packageInfos packageId + if versions.Count > 1 then + yield packageId + } + if not (packageIdsWithMoreThan1Version.Any()) then + Map.empty + else + seq { + for pkgId in packageIdsWithMoreThan1Version do + let pkgs = seq { + for KeyValue (file, packageInfos) in packageTree do + for pkg in packageInfos do + if pkg.PackageId = pkgId then + yield file, pkg + } + yield pkgId, pkgs + } |> Map.ofSeq + + let packageTree = getPackageTree sol + let packages = getAllPackageIdsAndVersions packageTree + Console.WriteLine(sprintf "%d nuget packages found for solution %s" packages.Count sol.Name) + let idealDirList = getDirectoryNamesForPackagesSet packages + + let solDir = sol.Directory + solDir.Refresh () + let missingPackageDirs = findMissingPackageDirs solDir idealDirList + if missingPackageDirs.Any () then + for KeyValue(missingPkg, depHolders) in missingPackageDirs do + let depHolderNames = String.Join(",", depHolders.Select(fun dh -> dh.Name)) + Console.Error.WriteLine (sprintf "Missing folder for nuget package in submodule: %s (referenced from %s)" missingPkg depHolderNames) + Environment.Exit 1 + +#if LEGACY_FRAMEWORK + let excessPackageDirs = findExcessPackageDirs solDir idealDirList + if excessPackageDirs.Any () then + let advice = "remove it with git filter-branch to avoid needless bandwidth: http://stackoverflow.com/a/17824718/6503091" + for excessPkg in excessPackageDirs do + Console.Error.WriteLine(sprintf "Unused nuget package folder for solution dir %s: %s (%s)" solDir.Name excessPkg advice) + Environment.Exit 1 +#endif + + let pkgWithMoreThan1VersionPrint (key: string) (packageInfos: seq) = + Console.Error.WriteLine (sprintf "Package found with more than one version: %s. All occurrences:" key) + for file,pkgInfo in packageInfos do + Console.Error.WriteLine (sprintf "* Version: %s. Dependency holder: %s" pkgInfo.PackageVersion file.DependencyHolderName.Name) + let packagesWithMoreThanOneVersion = findPackagesWithMoreThanOneVersion packageTree + if packagesWithMoreThanOneVersion.Any() then + Map.iter pkgWithMoreThan1VersionPrint packagesWithMoreThanOneVersion + Environment.Exit 1 + + let findPackagesWithSomeReqReinstallAttrib + (packageTree: Map>) + : seq = + seq { + for KeyValue (file, packageInfos) in packageTree do + for pkg in packageInfos do + match pkg.ReqReinstall with + | Some true -> + yield file, pkg + | _ -> () + } + let packagesWithWithSomeReqReinstallAttrib = findPackagesWithSomeReqReinstallAttrib packageTree + if packagesWithWithSomeReqReinstallAttrib.Any() then + Console.Error.WriteLine ( + sprintf "Packages found with some RequireReinstall attribute (please reinstall it before pushing):" + ) + for file,pkg in packagesWithWithSomeReqReinstallAttrib do + Console.Error.WriteLine ( + sprintf "* Name: %s. Project: %s" pkg.PackageId file.DependencyHolderName.Name + ) + Environment.Exit 1 + + Console.WriteLine (sprintf "Nuget sanity check succeeded for solution dir %s" solDir.FullName) + + + let rec findSolutions (dir: DirectoryInfo): seq = + dir.Refresh () + seq { + // FIXME: avoid returning duplicates? (in case there are 2 .sln files in the same dir...) + for file in dir.EnumerateFiles () do + if file.Name.ToLower().EndsWith ".sln" then + yield file + for subdir in dir.EnumerateDirectories().Where notSubmodule do + for solution in findSolutions subdir do + yield solution + } + + //let solutions = Directory.GetCurrentDirectory() |> DirectoryInfo |> findSolutions + //NOTE: we hardcode the solutions rather than the line above, because e.g. Linux OS can't build/restore iOS proj + let solutionFileNames = [ + Path.Combine(FsxHelper.SourceDir.FullName, "gwallet.linux-legacy.sln") + Path.Combine(FsxHelper.SourceDir.FullName, "gwallet.mac-legacy.sln") + Path.Combine(FsxHelper.SourceDir.FullName, "gwallet.core-legacy.sln") + Path.Combine(FsxHelper.SourceDir.FullName, "gwallet.core.sln") + ] + + let solutionFiles = solutionFileNames |> List.map FileInfo + + let checkFilesExist (fileNames: List) = + fileNames + |> List.map (fun fileName -> fileName.Exists) + |> List.fold (&&) true + + let allFilesExist = checkFilesExist solutionFiles + + if not allFilesExist then + failwith "Solution files were not found to do sanity check." + + match Misc.GuessPlatform() with + // xbuild cannot build .NETStandard projects so we cannot build the non-Core parts: + | Misc.Platform.Linux when "dotnet" = Environment.GetEnvironmentVariable "BuildTool" -> + sanityCheckNugetPackagesFromSolution solutionFiles.[3] + | Misc.Platform.Linux when "msbuild" = Environment.GetEnvironmentVariable "LegacyBuildTool" -> + sanityCheckNugetPackagesFromSolution solutionFiles.[0] + // .NET-only macOS only builds gwallet.core.sln + | Misc.Platform.Mac when "dotnet" = Environment.GetEnvironmentVariable "BuildTool" + && Environment.GetEnvironmentVariable "LegacyBuildTool" = "" -> + sanityCheckNugetPackagesFromSolution solutionFiles.[3] + | Misc.Platform.Mac when "msbuild" = Environment.GetEnvironmentVariable "LegacyBuildTool" -> + sanityCheckNugetPackagesFromSolution solutionFiles.[1] + + | _ (* stockmono linux and windows *) -> + + // TODO: have a windows solution file + sanityCheckNugetPackagesFromSolution solutionFiles.[2] + + +FindOffendingPrintfUsage() +SanityCheckNugetPackages() +