diff options
Diffstat (limited to 'src/SMAPI.ModBuildConfig')
-rw-r--r-- | src/SMAPI.ModBuildConfig/DeployModTask.cs | 154 | ||||
-rw-r--r-- | src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs | 174 | ||||
-rw-r--r-- | src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs | 16 | ||||
-rw-r--r-- | src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs | 9 | ||||
-rw-r--r-- | src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj | 60 | ||||
-rw-r--r-- | src/SMAPI.ModBuildConfig/assets/nuget-icon.pdn | bin | 0 -> 7401 bytes | |||
-rw-r--r-- | src/SMAPI.ModBuildConfig/assets/nuget-icon.png | bin | 0 -> 5054 bytes | |||
-rw-r--r-- | src/SMAPI.ModBuildConfig/build/smapi.targets | 144 | ||||
-rw-r--r-- | src/SMAPI.ModBuildConfig/package.nuspec | 32 |
9 files changed, 589 insertions, 0 deletions
diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs new file mode 100644 index 00000000..a5725a81 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using StardewModdingAPI.ModBuildConfig.Framework; + +namespace StardewModdingAPI.ModBuildConfig +{ + /// <summary>A build task which deploys the mod files and prepares a release zip.</summary> + public class DeployModTask : Task + { + /********* + ** Accessors + *********/ + /// <summary>The name of the mod folder.</summary> + [Required] + public string ModFolderName { get; set; } + + /// <summary>The absolute or relative path to the folder which should contain the generated zip file.</summary> + [Required] + public string ModZipPath { get; set; } + + /// <summary>The folder containing the project files.</summary> + [Required] + public string ProjectDir { get; set; } + + /// <summary>The folder containing the build output.</summary> + [Required] + public string TargetDir { get; set; } + + /// <summary>The folder containing the game files.</summary> + [Required] + public string GameDir { get; set; } + + /// <summary>Whether to enable copying the mod files into the game's Mods folder.</summary> + [Required] + public bool EnableModDeploy { get; set; } + + /// <summary>Whether to enable the release zip.</summary> + [Required] + public bool EnableModZip { get; set; } + + + /********* + ** Public methods + *********/ + /// <summary>When overridden in a derived class, executes the task.</summary> + /// <returns>true if the task successfully executed; otherwise, false.</returns> + public override bool Execute() + { + if (!this.EnableModDeploy && !this.EnableModZip) + return true; // nothing to do + + try + { + // get mod info + ModFileManager package = new ModFileManager(this.ProjectDir, this.TargetDir); + + // deploy mod files + if (this.EnableModDeploy) + { + string outputPath = Path.Combine(this.GameDir, "Mods", this.EscapeInvalidFilenameCharacters(this.ModFolderName)); + this.Log.LogMessage(MessageImportance.High, $"The mod build package is copying the mod files to {outputPath}..."); + this.CreateModFolder(package.GetFiles(), outputPath); + } + + // create release zip + if (this.EnableModZip) + { + this.Log.LogMessage(MessageImportance.High, $"The mod build package is generating a release zip at {this.ModZipPath} for {this.ModFolderName}..."); + this.CreateReleaseZip(package.GetFiles(), this.ModFolderName, package.GetManifestVersion(), this.ModZipPath); + } + + return true; + } + catch (UserErrorException ex) + { + this.Log.LogErrorFromException(ex); + return false; + } + catch (Exception ex) + { + this.Log.LogError($"The mod build package failed trying to deploy the mod.\n{ex}"); + return false; + } + } + + + /********* + ** Private methods + *********/ + /// <summary>Copy the mod files into the game's mod folder.</summary> + /// <param name="files">The files to include.</param> + /// <param name="modFolderPath">The folder path to create with the mod files.</param> + private void CreateModFolder(IDictionary<string, FileInfo> files, string modFolderPath) + { + foreach (var entry in files) + { + string fromPath = entry.Value.FullName; + string toPath = Path.Combine(modFolderPath, entry.Key); + + // ReSharper disable once AssignNullToNotNullAttribute -- not applicable in this context + Directory.CreateDirectory(Path.GetDirectoryName(toPath)); + + File.Copy(fromPath, toPath, overwrite: true); + } + } + + /// <summary>Create a release zip in the recommended format for uploading to mod sites.</summary> + /// <param name="files">The files to include.</param> + /// <param name="modName">The name of the mod.</param> + /// <param name="modVersion">The mod version string.</param> + /// <param name="outputFolderPath">The absolute or relative path to the folder which should contain the generated zip file.</param> + private void CreateReleaseZip(IDictionary<string, FileInfo> files, string modName, string modVersion, string outputFolderPath) + { + // get names + string zipName = this.EscapeInvalidFilenameCharacters($"{modName} {modVersion}.zip"); + string folderName = this.EscapeInvalidFilenameCharacters(modName); + string zipPath = Path.Combine(outputFolderPath, zipName); + + // create zip file + Directory.CreateDirectory(outputFolderPath); + using (Stream zipStream = new FileStream(zipPath, FileMode.Create, FileAccess.Write)) + using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create)) + { + foreach (var fileEntry in files) + { + string relativePath = fileEntry.Key; + FileInfo file = fileEntry.Value; + + // get file info + string filePath = file.FullName; + string entryName = folderName + '/' + relativePath.Replace(Path.DirectorySeparatorChar, '/'); + + // add to zip + using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + using (Stream fileStreamInZip = archive.CreateEntry(entryName).Open()) + fileStream.CopyTo(fileStreamInZip); + } + } + } + + /// <summary>Get a copy of a filename with all invalid filename characters substituted.</summary> + /// <param name="name">The filename.</param> + private string EscapeInvalidFilenameCharacters(string name) + { + foreach (char invalidChar in Path.GetInvalidFileNameChars()) + name = name.Replace(invalidChar, '.'); + return name; + } + } +} diff --git a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs new file mode 100644 index 00000000..64262dc2 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Web.Script.Serialization; +using StardewModdingAPI.Common; + +namespace StardewModdingAPI.ModBuildConfig.Framework +{ + /// <summary>Manages the files that are part of a mod package.</summary> + internal class ModFileManager + { + /********* + ** Properties + *********/ + /// <summary>The name of the manifest file.</summary> + private readonly string ManifestFileName = "manifest.json"; + + /// <summary>The files that are part of the package.</summary> + private readonly IDictionary<string, FileInfo> Files; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="projectDir">The folder containing the project files.</param> + /// <param name="targetDir">The folder containing the build output.</param> + /// <exception cref="UserErrorException">The mod package isn't valid.</exception> + public ModFileManager(string projectDir, string targetDir) + { + this.Files = new Dictionary<string, FileInfo>(StringComparer.InvariantCultureIgnoreCase); + + // validate paths + if (!Directory.Exists(projectDir)) + throw new UserErrorException("Could not create mod package because the project folder wasn't found."); + if (!Directory.Exists(targetDir)) + throw new UserErrorException("Could not create mod package because no build output was found."); + + // project manifest + bool hasProjectManifest = false; + { + FileInfo manifest = new FileInfo(Path.Combine(projectDir, "manifest.json")); + if (manifest.Exists) + { + this.Files[this.ManifestFileName] = manifest; + hasProjectManifest = true; + } + } + + // project i18n files + bool hasProjectTranslations = false; + DirectoryInfo translationsFolder = new DirectoryInfo(Path.Combine(projectDir, "i18n")); + if (translationsFolder.Exists) + { + foreach (FileInfo file in translationsFolder.EnumerateFiles()) + this.Files[Path.Combine("i18n", file.Name)] = file; + hasProjectTranslations = true; + } + + // build output + DirectoryInfo buildFolder = new DirectoryInfo(targetDir); + foreach (FileInfo file in buildFolder.EnumerateFiles("*", SearchOption.AllDirectories)) + { + // get relative paths + string relativePath = file.FullName.Replace(buildFolder.FullName, ""); + string relativeDirPath = file.Directory.FullName.Replace(buildFolder.FullName, ""); + + // prefer project manifest/i18n files + if (hasProjectManifest && this.EqualsInvariant(relativePath, this.ManifestFileName)) + continue; + if (hasProjectTranslations && this.EqualsInvariant(relativeDirPath, "i18n")) + continue; + + // ignore release zips + if (this.EqualsInvariant(file.Extension, ".zip")) + continue; + + // ignore Json.NET (bundled into SMAPI) + if (this.EqualsInvariant(file.Name, "Newtonsoft.Json.dll") || this.EqualsInvariant(file.Name, "Newtonsoft.Json.xml")) + continue; + + // add file + this.Files[relativePath] = file; + } + + // check for missing manifest + if (!this.Files.ContainsKey(this.ManifestFileName)) + throw new UserErrorException($"Could not create mod package because no {this.ManifestFileName} was found in the project or build output."); + + // check for missing DLL + // ReSharper disable once SimplifyLinqExpression + if (!this.Files.Any(p => !p.Key.EndsWith(".dll"))) + throw new UserErrorException("Could not create mod package because no .dll file was found in the project or build output."); + } + + /// <summary>Get the files in the mod package.</summary> + public IDictionary<string, FileInfo> GetFiles() + { + return new Dictionary<string, FileInfo>(this.Files, StringComparer.InvariantCultureIgnoreCase); + } + + /// <summary>Get a semantic version from the mod manifest.</summary> + /// <exception cref="UserErrorException">The manifest is missing or invalid.</exception> + public string GetManifestVersion() + { + // get manifest file + if (!this.Files.TryGetValue(this.ManifestFileName, out FileInfo manifestFile)) + throw new InvalidOperationException($"The mod does not have a {this.ManifestFileName} file."); // shouldn't happen since we validate in constructor + + // read content + string json = File.ReadAllText(manifestFile.FullName); + if (string.IsNullOrWhiteSpace(json)) + throw new UserErrorException("The mod's manifest must not be empty."); + + // parse JSON + IDictionary<string, object> data; + try + { + data = this.Parse(json); + } + catch (Exception ex) + { + throw new UserErrorException($"The mod's manifest couldn't be parsed. It doesn't seem to be valid JSON.\n{ex}"); + } + + // get version field + object versionObj = data.ContainsKey("Version") ? data["Version"] : null; + if (versionObj == null) + throw new UserErrorException("The mod's manifest must have a version field."); + + // get version string + if (versionObj is IDictionary<string, object> versionFields) // SMAPI 1.x + { + int major = versionFields.ContainsKey("MajorVersion") ? (int)versionFields["MajorVersion"] : 0; + int minor = versionFields.ContainsKey("MinorVersion") ? (int)versionFields["MinorVersion"] : 0; + int patch = versionFields.ContainsKey("PatchVersion") ? (int)versionFields["PatchVersion"] : 0; + string tag = versionFields.ContainsKey("Build") ? (string)versionFields["Build"] : null; + return new SemanticVersionImpl(major, minor, patch, tag).ToString(); + } + return new SemanticVersionImpl(versionObj.ToString()).ToString(); // SMAPI 2.0+ + } + + + /********* + ** Private methods + *********/ + /// <summary>Get a case-insensitive dictionary matching the given JSON.</summary> + /// <param name="json">The JSON to parse.</param> + private IDictionary<string, object> Parse(string json) + { + IDictionary<string, object> MakeCaseInsensitive(IDictionary<string, object> dict) + { + foreach (var field in dict.ToArray()) + { + if (field.Value is IDictionary<string, object> value) + dict[field.Key] = MakeCaseInsensitive(value); + } + return new Dictionary<string, object>(dict, StringComparer.InvariantCultureIgnoreCase); + } + + IDictionary<string, object> data = (IDictionary<string, object>)new JavaScriptSerializer().DeserializeObject(json); + return MakeCaseInsensitive(data); + } + + /// <summary>Get whether a string is equal to another case-insensitively.</summary> + /// <param name="str">The string value.</param> + /// <param name="other">The string to compare with.</param> + private bool EqualsInvariant(string str, string other) + { + return str.Equals(other, StringComparison.InvariantCultureIgnoreCase); + } + } +} diff --git a/src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs b/src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs new file mode 100644 index 00000000..64e31c29 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs @@ -0,0 +1,16 @@ +using System; + +namespace StardewModdingAPI.ModBuildConfig.Framework +{ + /// <summary>A user error whose message can be displayed to the user.</summary> + internal class UserErrorException : Exception + { + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="message">The error message.</param> + public UserErrorException(string message) + : base(message) { } + } +} diff --git a/src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs b/src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..6ef2d568 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/Properties/AssemblyInfo.cs @@ -0,0 +1,9 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("StardewModdingAPI.ModBuildConfig")] +[assembly: AssemblyDescription("")] +[assembly: Guid("ea4f1e80-743f-4a1d-9757-ae66904a196a")] +[assembly: ComVisible(false)] +[assembly: AssemblyVersion("2.0.1.0")] +[assembly: AssemblyFileVersion("2.0.1.0")] diff --git a/src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj b/src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj new file mode 100644 index 00000000..e04f09a7 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProjectGuid>{EA4F1E80-743F-4A1D-9757-AE66904A196A}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>StardewModdingAPI.ModBuildConfig</RootNamespace> + <AssemblyName>StardewModdingAPI.ModBuildConfig</AssemblyName> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.Build" /> + <Reference Include="Microsoft.Build.Framework" /> + <Reference Include="Microsoft.Build.Utilities.v4.0" /> + <Reference Include="System" /> + <Reference Include="System.IO.Compression" /> + <Reference Include="System.Web.Extensions" /> + </ItemGroup> + <ItemGroup> + <Compile Include="DeployModTask.cs" /> + <Compile Include="Framework\UserErrorException.cs" /> + <Compile Include="Framework\ModFileManager.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="assets\nuget-icon.pdn" /> + <None Include="build\smapi.targets"> + <SubType>Designer</SubType> + </None> + <None Include="package.nuspec"> + <SubType>Designer</SubType> + </None> + </ItemGroup> + <ItemGroup> + <Content Include="assets\nuget-icon.png" /> + </ItemGroup> + <Import Project="..\SMAPI.Common\StardewModdingAPI.Common.projitems" Label="Shared" /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project>
\ No newline at end of file diff --git a/src/SMAPI.ModBuildConfig/assets/nuget-icon.pdn b/src/SMAPI.ModBuildConfig/assets/nuget-icon.pdn Binary files differnew file mode 100644 index 00000000..7bd5c0c5 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/assets/nuget-icon.pdn diff --git a/src/SMAPI.ModBuildConfig/assets/nuget-icon.png b/src/SMAPI.ModBuildConfig/assets/nuget-icon.png Binary files differnew file mode 100644 index 00000000..611cdf88 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/assets/nuget-icon.png diff --git a/src/SMAPI.ModBuildConfig/build/smapi.targets b/src/SMAPI.ModBuildConfig/build/smapi.targets new file mode 100644 index 00000000..c0319e22 --- /dev/null +++ b/src/SMAPI.ModBuildConfig/build/smapi.targets @@ -0,0 +1,144 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--********************************************* + ** Import build tasks + **********************************************--> + <UsingTask TaskName="DeployModTask" AssemblyFile="StardewModdingAPI.ModBuildConfig.dll" /> + + <!--********************************************* + ** Find the basic mod metadata + **********************************************--> + <!-- import developer's custom settings (if any) --> + <Import Condition="$(OS) != 'Windows_NT' AND Exists('$(HOME)\stardewvalley.targets')" Project="$(HOME)\stardewvalley.targets" /> + <Import Condition="$(OS) == 'Windows_NT' AND Exists('$(USERPROFILE)\stardewvalley.targets')" Project="$(USERPROFILE)\stardewvalley.targets" /> + + <!-- set setting defaults --> + <PropertyGroup> + <!-- map legacy settings --> + <ModFolderName Condition="'$(ModFolderName)' == '' AND '$(DeployModFolderName)' != ''">$(DeployModFolderName)</ModFolderName> + <ModZipPath Condition="'$(ModZipPath)' == '' AND '$(DeployModZipTo)' != ''">$(DeployModZipTo)</ModZipPath> + + <!-- set default settings --> + <ModFolderName Condition="'$(ModFolderName)' == ''">$(MSBuildProjectName)</ModFolderName> + <ModZipPath Condition="'$(ModZipPath)' == ''">$(TargetDir)</ModZipPath> + <EnableModDeploy Condition="'$(EnableModDeploy)' == ''">True</EnableModDeploy> + <EnableModZip Condition="'$(EnableModZip)' == ''">True</EnableModZip> + </PropertyGroup> + + <!-- find platform + game path --> + <Choose> + <When Condition="$(OS) == 'Unix' OR $(OS) == 'OSX'"> + <PropertyGroup> + <!-- Linux --> + <GamePath Condition="!Exists('$(GamePath)')">$(HOME)/GOG Games/Stardew Valley/game</GamePath> + <GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath> + + <!-- Mac (may be 'Unix' or 'OSX') --> + <GamePath Condition="!Exists('$(GamePath)')">/Applications/Stardew Valley.app/Contents/MacOS</GamePath> + <GamePath Condition="!Exists('$(GamePath)')">$(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS</GamePath> + </PropertyGroup> + </When> + <When Condition="$(OS) == 'Windows_NT'"> + <PropertyGroup> + <GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath> + <GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath> + <GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32))</GamePath> + <GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32))</GamePath> + </PropertyGroup> + </When> + </Choose> + + + <!--********************************************* + ** Inject the assembly references and debugging configuration + **********************************************--> + <Choose> + <When Condition="$(OS) == 'Windows_NT'"> + <!-- references --> + <ItemGroup> + <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> + <Private>false</Private> + </Reference> + <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> + <Private>false</Private> + </Reference> + <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> + <Private>false</Private> + </Reference> + <Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> + <Private>false</Private> + </Reference> + <Reference Include="Stardew Valley"> + <HintPath>$(GamePath)\Stardew Valley.exe</HintPath> + <Private>false</Private> + </Reference> + <Reference Include="StardewModdingAPI"> + <HintPath>$(GamePath)\StardewModdingAPI.exe</HintPath> + <Private>false</Private> + </Reference> + <Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86"> + <HintPath>$(GamePath)\xTile.dll</HintPath> + <Private>false</Private> + <SpecificVersion>False</SpecificVersion> + </Reference> + </ItemGroup> + + <!-- launch game for debugging --> + <PropertyGroup> + <StartAction>Program</StartAction> + <StartProgram>$(GamePath)\StardewModdingAPI.exe</StartProgram> + <StartWorkingDirectory>$(GamePath)</StartWorkingDirectory> + </PropertyGroup> + </When> + <Otherwise> + <!-- references --> + <ItemGroup> + <Reference Include="MonoGame.Framework"> + <HintPath>$(GamePath)\MonoGame.Framework.dll</HintPath> + <Private>false</Private> + <SpecificVersion>False</SpecificVersion> + </Reference> + <Reference Include="StardewValley"> + <HintPath>$(GamePath)\StardewValley.exe</HintPath> + <Private>false</Private> + </Reference> + <Reference Include="StardewModdingAPI"> + <HintPath>$(GamePath)\StardewModdingAPI.exe</HintPath> + <Private>false</Private> + </Reference> + <Reference Include="xTile"> + <HintPath>$(GamePath)\xTile.dll</HintPath> + <Private>false</Private> + </Reference> + </ItemGroup> + </Otherwise> + </Choose> + + + <!--********************************************* + ** Deploy mod files & create release zip after build + **********************************************--> + <!-- if game path or OS is invalid, show one user-friendly error instead of a slew of reference errors --> + <Target Name="BeforeBuild"> + <Error Condition="'$(OS)' != 'OSX' AND '$(OS)' != 'Unix' AND '$(OS)' != 'Windows_NT'" Text="The mod build package doesn't recognise OS type '$(OS)'." /> + + <Error Condition="!Exists('$(GamePath)')" Text="The mod build package can't find your game folder. You can specify where to find it; see details at https://github.com/Pathoschild/SMAPI/blob/develop/docs/mod-build-config.md#game-path." /> + <Error Condition="'$(OS)' == 'Windows_NT' AND !Exists('$(GamePath)\Stardew Valley.exe')" Text="The mod build package found a a game folder at $(GamePath), but it doesn't contain the Stardew Valley.exe file. If this folder is invalid, delete it and the package will autodetect another game install path." /> + <Error Condition="'$(OS)' != 'Windows_NT' AND !Exists('$(GamePath)\StardewValley.exe')" Text="The mod build package found a a game folder at $(GamePath), but it doesn't contain the StardewValley.exe file. If this folder is invalid, delete it and the package will autodetect another game install path." /> + <Error Condition="!Exists('$(GamePath)\StardewModdingAPI.exe')" Text="The mod build package found a game folder at $(GamePath), but it doesn't contain SMAPI. You need to install SMAPI before building the mod." /> + </Target> + + <!-- deploy mod files & create release zip --> + <Target Name="AfterBuild"> + <DeployModTask + ModFolderName="$(ModFolderName)" + ModZipPath="$(ModZipPath)" + + EnableModDeploy="$(EnableModDeploy)" + EnableModZip="$(EnableModZip)" + + ProjectDir="$(ProjectDir)" + TargetDir="$(TargetDir)" + GameDir="$(GamePath)" + /> + </Target> +</Project> diff --git a/src/SMAPI.ModBuildConfig/package.nuspec b/src/SMAPI.ModBuildConfig/package.nuspec new file mode 100644 index 00000000..b1b228de --- /dev/null +++ b/src/SMAPI.ModBuildConfig/package.nuspec @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> + <metadata> + <id>Pathoschild.Stardew.ModBuildConfig</id> + <version>2.0.1</version> + <title>Build package for SMAPI mods</title> + <authors>Pathoschild</authors> + <owners>Pathoschild</owners> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <licenseUrl>https://github.com/Pathoschild/SMAPI/blob/develop/LICENSE.txt</licenseUrl> + <projectUrl>https://github.com/Pathoschild/SMAPI/blob/develop/docs/mod-build-config.md#readme</projectUrl> + <iconUrl>https://raw.githubusercontent.com/Pathoschild/SMAPI/develop/src/SMAPI.ModBuildConfig/assets/nuget-icon.png</iconUrl> + <description>Automates the build configuration for crossplatform Stardew Valley SMAPI mods.</description> + <releaseNotes> + 2.0: + - Added: mods are now copied into the `Mods` folder automatically (configurable). + - Added: release zips are now created automatically in your build output folder (configurable). + - Added: mod deploy and release zips now exclude Json.NET automatically, since it's provided by SMAPI. + - Added mod's version to release zip filename. + - Improved errors to simplify troubleshooting. + - Fixed release zip not having a mod folder. + - Fixed release zip failing if mod name contains characters that aren't valid in a filename. + + 2.0.1: + - Fixed mod deploy failing to create subfolders if they don't already exist. + </releaseNotes> + </metadata> + <files> + <file src="build/smapi.targets" target="build/Pathoschild.Stardew.ModBuildConfig.targets" /> + <file src="bin/StardewModdingAPI.ModBuildConfig.dll" target="build/StardewModdingAPI.ModBuildConfig.dll" /> + </files> +</package> |