- Home /
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("\n"[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;
}
}
Do the "ios" and "android" folders exist before building?
Alright so if I make sure the android folder exists before building it works, but doing the same for iOS changed nothing. I still get the same error with iOS.
To be honest, it's a little confusing that certain builds create their own directories and others do not. The output of each "build" isn't very well documented in the BuildPipeline's BuildPlayer function documentation or in BuildTarget either.
Answer by ryan_unity · Jul 31, 2015 at 10:22 AM
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 :)
Docs - BuildOptions.AcceptExternalModificationsToPlayer: http://docs.unity3d.com/ScriptReference/BuildOptions.AcceptExternalModificationsToPlayer.html
EDIT: Also, don't put a slash at the end of the path for the iOS build. Change path + "/ios/" to path + "/ios"
$$anonymous$$aking sure the folder exists for Android works. For iOS, making sure the folder exists and setting BuildOptions to BuildOptions.None
still does not resolve the issue. In fact, when the iOS build runs with BuildOptions.None
and the folder exists, at some point the folder gets deleted and that same error gets thrown.
Hi, I've had another look and you also shouldn't have a slash at the end of your ios path. Change path + "/ios/"
to path + "/ios"
I've tested this my end and it works. If it fixes your problem I'll update the answer :)
I can indeed confirm that this works. Thank you very much for your assistance, and I would highly recommend that you pass this feedback on to whomever is in charge of documentation, because all of this could've been avoided with better documentation.
Things like certain build outputs requiring folders to exist and others not requiring them, certain build outputs accepting forward slashes at the end of the path and others not accepting that, all of this should be documented.
Awesome! I've updated the answer in case other people stumble across this. I'll forward your feedback to the documentation $$anonymous$$m as well :)