Unable to build to Android and iOS using BuildPipeline Failed to copy files

I get errors related to copying files when attempting to use BuildPipeline with iOS or Android (it works fine with OS X and Windows).

Here is the iOS error I get:

IOException: Failed to Copy File / Directory from 'Temp/StagingArea/Trampoline/Unity-iPhone.xcodeproj/project.pbxproj' to '/Users/wuchiang/Desktop/mygame-build/ios/Unity-iPhone.xcodeproj/project.pbxproj'.
UnityEditor.iOS.Utils.ReplaceFileOrDirectoryCopy (System.String src, System.String dst)
UnityEditor.iOS.PostProcessiPhonePlayer.UpdateInstallLocation (UnityEditor.iOS.ProjectPaths paths, ScriptingImplementation backend, BuildOptions options, UnityEditor.iOS.IncludedFileList includedFiles)
UnityEditor.iOS.PostProcessiPhonePlayer.PostProcess (UnityEditor.iOS.iOSBuildPostprocessor pp, BuildTarget target, System.String stagingAreaData, System.String stagingArea, System.String stagingAreaDataManaged, System.String playerPackage, System.String installPath, System.String companyName, System.String productName, BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry)
UnityEditor.iOS.iOSBuildPostprocessor.PostProcess (BuildPostProcessArgs args)
UnityEditor.PostprocessBuildPlayer.Postprocess (BuildTarget target, System.String installPath, System.String companyName, System.String productName, Int32 width, Int32 height, System.String downloadWebplayerUrl, System.String manualDownloadWebplayerUrl, BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry) (at /Users/builduser/buildslave/unity/build/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs:316)
UnityEditor.BuildPipeline:BuildPlayer(String[], String, BuildTarget, BuildOptions)
BuildForAllPlatforms:BuildGame() (at Assets/GameAssets/Scripts/Editor/BuildForAllPlatforms.cs:67)

and here is the Android error I get:

DirectoryNotFoundException: Could not find a part of the path "/Users/wuchiang/Desktop/mygame-build/android/MyGame.apk".
System.IO.File.Delete (System.String path) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/File.cs:173)
UnityEditor.Android.PostProcessAndroidPlayer.PostProcessInternal (BuildTarget target, System.String stagingAreaData, System.String stagingArea, System.String playerPackage, System.String installPath, System.String companyName, System.String productName, BuildOptions options)
UnityEditor.Android.PostProcessAndroidPlayer.PostProcess (BuildTarget target, System.String stagingAreaData, System.String stagingArea, System.String playerPackage, System.String installPath, System.String companyName, System.String productName, BuildOptions options)
UnityEditor.Android.AndroidBuildPostprocessor.PostProcess (BuildPostProcessArgs args)
UnityEditor.PostprocessBuildPlayer.Postprocess (BuildTarget target, System.String installPath, System.String companyName, System.String productName, Int32 width, Int32 height, System.String downloadWebplayerUrl, System.String manualDownloadWebplayerUrl, BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry) (at /Users/builduser/buildslave/unity/build/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs:316)
UnityEditor.BuildPipeline:BuildPlayer(String[], String, BuildTarget, BuildOptions)
BuildForAllPlatforms:BuildGame() (at Assets/GameAssets/Scripts/Editor/BuildForAllPlatforms.cs:90)

Error building Player: DirectoryNotFoundException: Could not find a part of the path "/Users/wuchiang/Desktop/mygame-build/android/MyGame.apk".

Here is my script:

using UnityEditor;
using UnityEngine;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;

public class BuildForAllPlatforms
{
	private static int VersionCode = 4;
	private static string Version = "1.0.4";
	private static string GameName = "MyGame";
	private static ApiCompatibilityLevel ApiCompatLevel = ApiCompatibilityLevel.NET_2_0;

	// iOS
	private static string iOSBundleIdentifier = "com.unity.answers";
	private static iOSTargetDevice iOSTargetDev = iOSTargetDevice.iPhoneAndiPad;
	private static iOSTargetOSVersion iOSTargetVer = iOSTargetOSVersion.iOS_8_0;

	// Android
	private static string AndroidBundleIdentifier = "com.unity.answers";
	private static AndroidSdkVersions AndroidMinSdk = AndroidSdkVersions.AndroidApiLevel16; // Jellybean;

	[MenuItem("Custom Build/Build for all platforms %&b")]
	public static void BuildGame()
	{
		// Get build path
		string path = EditorUtility.SaveFolderPanel("Choose Location of Built Game", "", "");
		string[] levels = GetLevelPaths();
		
		// Build win32 player.
		BuildPipeline.BuildPlayer
		(
			levels,
			path + "/win32/" + GameName + ".exe",
			BuildTarget.StandaloneWindows,
			BuildOptions.None
		);

		// Build osx64 player
		BuildPipeline.BuildPlayer
		(
			levels,
		    path + "/osx64/" + GameName + ".app",
		    BuildTarget.StandaloneOSXIntel64,
			BuildOptions.None
		);

		// Setup settings shared between Android and iOS
		PlayerSettings.bundleVersion = Version;
		PlayerSettings.apiCompatibilityLevel = ApiCompatLevel;

		// Setup settings for iOS player
		PlayerSettings.iPhoneBundleIdentifier = iOSBundleIdentifier;
		PlayerSettings.iOS.targetDevice = iOSTargetDev;
		PlayerSettings.iOS.targetOSVersion = iOSTargetVer;

		// Build iOS player
		BuildPipeline.BuildPlayer
		(
			levels,
			path + "/ios/",
			BuildTarget.iOS,
			BuildOptions.Il2CPP | BuildOptions.AcceptExternalModificationsToPlayer
		);

		// Setup settings for Android player
		PlayerSettings.Android.bundleVersionCode = VersionCode;
		PlayerSettings.Android.minSdkVersion = AndroidMinSdk;
		PlayerSettings.Android.androidIsGame = true;
		PlayerSettings.bundleIdentifier = AndroidBundleIdentifier;
		
		// Read and setup keystore data
		Dictionary<string, string> keystoreData = GetAndroidKeystoreData();

		PlayerSettings.Android.keystoreName = keystoreData["MYGAME_ANDROID_KEYSTORE_PATH"];
		PlayerSettings.Android.keystorePass = keystoreData["MYGAME_ANDROID_KEYSTORE_PASSWORD"];
		PlayerSettings.Android.keyaliasName = keystoreData["MYGAME_ANDROID_KEY_ALIAS_NAME"];
		PlayerSettings.Android.keyaliasPass = keystoreData["MYGAME_ANDROID_KEY_ALIAS_PASSWORD"];

		// Build Android player
		BuildPipeline.BuildPlayer
		(
			levels,
			path + "/android/" + GameName + ".apk",
			BuildTarget.Android,
			BuildOptions.None
		);
	}
	
	private static string[] GetLevelPaths()
	{
		List<string> temp = new List<string>();

		foreach (UnityEditor.EditorBuildSettingsScene scene in UnityEditor.EditorBuildSettings.scenes)
		{
			if (scene.enabled)
			{
				temp.Add(scene.path);
			}
		}

		return temp.ToArray();
	}

	private static Dictionary<string, string> GetAndroidKeystoreData()
	{
		string homePath = System.Environment.GetEnvironmentVariable("HOME") + "/";
		string configFileName = ".mygame_config";

		var sr = new StreamReader(homePath + configFileName);
		var fileContents = sr.ReadToEnd();
		sr.Close();

		Dictionary<string, string> keystoreData = new Dictionary<string, string>();

		var lines = fileContents.Split("

"[0]);

		foreach (string line in lines)
		{
			if (line.Length == 0)
			{
				continue;
			}

			int indexOfEql = line.IndexOf('=');
			string option = line.Substring(0, indexOfEql);
			string value = line.Substring(indexOfEql + 1);

			keystoreData.Add(option, value);
		}

		return keystoreData;
	}
}

For Android it looks like the path doesn’t exist.

For iOS the BuildOptions.AcceptExternalModificationsToPlayer is for appending and will only work if there is already a project to append to at the path location. It looks like this might be the reason you’re receiving the iOS error. You can remove this build option or use BuildOptions.None for when you don’t have a project to append to and need one created. Also (just like for Android) make sure the path exists prior to building.

I hope this helps :slight_smile:

Docs - BuildOptions.AcceptExternalModificationsToPlayer:

EDIT: Also, don’t put a slash at the end of the path for the iOS build. Change path + “/ios/” to path + “/ios”