Skip to content

Commit 98b56cf

Browse files
committed
Merge pull request #7 from adjust/development
Added build scripts for iOS and Android
2 parents 55b072d + b16689f commit 98b56cf

11 files changed

+1852
-10
lines changed

Adjust.unitypackage

15.9 KB
Binary file not shown.

Assets/Editor.meta

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Editor/AdjustEditor.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using UnityEngine;
2+
using System.Collections;
3+
using UnityEditor.Callbacks;
4+
using UnityEditor;
5+
using System.Diagnostics;
6+
7+
public class AdjustEditor : MonoBehaviour {
8+
9+
[PostProcessBuild]
10+
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) {
11+
var exitCode = RunPostBuildScript (preBuild: false, pathToBuiltProject: pathToBuiltProject);
12+
13+
if (exitCode == -1) {
14+
return;
15+
}
16+
if (exitCode != 0) {
17+
var errorMessage = GenerateErrorScriptMessage(exitCode);
18+
19+
UnityEngine.Debug.LogError ("adjust: " + errorMessage);
20+
}
21+
}
22+
23+
[MenuItem("Adjust/Fix AndroidManifest.xml")]
24+
static void FixAndroidManifest() {
25+
#if UNITY_ANDROID
26+
var exitCode = RunPostBuildScript (preBuild: true);
27+
if (exitCode == 1) {
28+
EditorUtility.DisplayDialog("Adjust",
29+
string.Format("AndroidManifest.xml changed or created at {0}/Plugins/Android/ .",Application.dataPath),
30+
"OK");
31+
} else if (exitCode == 0) {
32+
EditorUtility.DisplayDialog("Adjust",
33+
"AndroidManifest.xml did not needed to be changed.",
34+
"OK");
35+
} else {
36+
EditorUtility.DisplayDialog("Adjust",
37+
GenerateErrorScriptMessage(exitCode),
38+
"OK");
39+
40+
}
41+
#else
42+
EditorUtility.DisplayDialog("Adjust", "Option only valid for the Android platform.", "OK");
43+
#endif
44+
}
45+
46+
static int RunPostBuildScript (bool preBuild, string pathToBuiltProject = "") {
47+
string pathToScript = null;
48+
string arguments = null;
49+
50+
#if UNITY_ANDROID
51+
pathToScript = "/Editor/AdjustPostBuildAndroid";
52+
arguments = Application.dataPath;
53+
if (preBuild)
54+
arguments = "--pre-build " + arguments;
55+
#elif UNITY_IOS
56+
pathToScript = "/Editor/AdjustPostBuildiOS";
57+
arguments = pathToBuiltProject;
58+
#else
59+
return -1;
60+
#endif
61+
62+
Process proc = new Process();
63+
proc.EnableRaisingEvents=false;
64+
proc.StartInfo.FileName = Application.dataPath + pathToScript;
65+
proc.StartInfo.Arguments = arguments;
66+
proc.Start();
67+
proc.WaitForExit();
68+
return proc.ExitCode;
69+
}
70+
71+
static string GenerateErrorScriptMessage(int exitCode) {
72+
#if UNITY_ANDROID
73+
if (exitCode == 1) {
74+
return "The AndroidManifest.xml file was only changed or created after building the package. " +
75+
"PLease build again the Android Unity package so it can use the new file";
76+
}
77+
#endif
78+
79+
if (exitCode != 0) {
80+
var message = "Build script exited with error." +
81+
" Please check the Adjust log file for more information at {0}";
82+
string projectPath = Application.dataPath.Substring (0, Application.dataPath.Length - 7);
83+
string logFile = null;
84+
#if UNITY_ANDROID
85+
logFile = projectPath + "/AdjustPostBuildAndroidLog.txt";
86+
#elif UNITY_IOS
87+
logFile = projectPath + "/AdjustPostBuiliOSLog.txt";
88+
#else
89+
return null;
90+
#endif
91+
return string.Format(message, logFile);
92+
}
93+
94+
return null;
95+
}
96+
}

Assets/Editor/AdjustEditor.cs.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Editor/AdjustPostBuildAndroid

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
import errno
5+
import shutil
6+
import os.path
7+
import sys
8+
from xml.dom.minidom import parse, parseString
9+
10+
def main():
11+
parser = argparse.ArgumentParser(
12+
description="Adjust post build android script")
13+
parser.add_argument('assets_path',
14+
help="path to the assets folder of unity3d")
15+
# pre build must be set manually
16+
parser.add_argument('--pre-build', action="store_true",
17+
help="used to check and change the AndroidManifest.xml to conform to the Adjust SDK")
18+
exit_code = 0
19+
with open('AdjustPostBuildAndroidLog.txt','w') as fileLog:
20+
# log function with file injected
21+
LogFunc = LogInput(fileLog)
22+
23+
# get the path of the android plugin folder
24+
android_plugin_path, pre_build = parse_input(LogFunc, parser)
25+
26+
# try to open an existing manifest file
27+
try:
28+
manifest_path = os.path.join(android_plugin_path + "AndroidManifest.xml")
29+
edited_xml = None
30+
with open(manifest_path,'r+') as mf:
31+
check_dic = check_manifest(LogFunc, mf)
32+
# check if manifest has all changes needed
33+
all_check = check_dic["has_adjust_receiver"] and \
34+
check_dic["has_internet_permission"] and \
35+
check_dic["has_wifi_permission"]
36+
# edit manifest if has any change missing
37+
if not all_check:
38+
# warn unity if it was pos-build, if something is missing
39+
if not pre_build:
40+
LogFunc("Android manifest used in unity did not " + \
41+
"had all the changes adjust SDK needs. " + \
42+
"Please build again the package.")
43+
edited_xml = edit_manifest(LogFunc, mf, check_dic)
44+
# write changed xml
45+
if edited_xml:
46+
with open(manifest_path,'w+') as mf:
47+
edited_xml.writexml(mf)
48+
exit_code = 1
49+
except IOError as ioe:
50+
# if it does not exist
51+
if ioe.errno == errno.ENOENT:
52+
# warn unity that needed manifest wasn't used
53+
if not pre_build:
54+
LogFunc("Used default Android manifest file from " + \
55+
"unity. Please build again the package to " +
56+
"include the changes for adjust SDK")
57+
copy_adjust_manifest(LogFunc, android_plugin_path)
58+
exit_code = 1
59+
else:
60+
LogFunc(ioe)
61+
except Exception as e:
62+
LogFunc(e)
63+
64+
# exit with return code for unity
65+
sys.exit(exit_code)
66+
67+
def edit_manifest(Log, manifest_file, check_dic):
68+
manifest_xml = check_dic["manifest_xml"]
69+
70+
# add the adjust install referrer to the application element
71+
if not check_dic["has_adjust_receiver"]:
72+
receiver_string = """<?xml version="1.0" ?>
73+
<receiver
74+
xmlns:android="http://schemas.android.com/apk/res/android"
75+
android:name="com.adjust.sdk.ReferrerReceiver"
76+
android:exported="true" >
77+
<intent-filter>
78+
<action android:name="com.android.vending.INSTALL_REFERRER" />
79+
</intent-filter>
80+
</receiver>
81+
"""
82+
receiver_xml = parseString(receiver_string)
83+
receiver_xml.documentElement.removeAttribute("xmlns:android")
84+
85+
for app_element in manifest_xml.getElementsByTagName("application"):
86+
app_element.appendChild(receiver_xml.documentElement)
87+
Log("added adjust install referrer receiver")
88+
89+
# add the internet permission to the manifest element
90+
if not check_dic["has_internet_permission"]:
91+
ip_element = manifest_xml.createElement("uses-permission")
92+
ip_element.setAttribute("android:name", "android.permission.INTERNET")
93+
manifest_xml.documentElement.appendChild(ip_element)
94+
Log("added internet permission")
95+
96+
# add the access wifi state permission to the manifest element
97+
if not check_dic["has_wifi_permission"]:
98+
ip_element = manifest_xml.createElement("uses-permission")
99+
ip_element.setAttribute("android:name", "android.permission.ACCESS_WIFI_STATE")
100+
manifest_xml.documentElement.appendChild(ip_element)
101+
Log("added access wifi permission")
102+
103+
#Log(manifest_xml.toxml())
104+
return manifest_xml
105+
106+
def check_manifest(Log, manifest_file):
107+
108+
manifest_xml = parse(manifest_file)
109+
#Log(manifest_xml.toxml())
110+
111+
has_adjust_receiver = has_element_attr(manifest_xml,
112+
"receiver", "android:name", "com.adjust.sdk.ReferrerReceiver")
113+
Log("has adjust install referrer receiver?: {0}", has_adjust_receiver)
114+
115+
has_internet_permission = has_element_attr(manifest_xml,
116+
"uses-permission", "android:name", "android.permission.INTERNET")
117+
Log("has internet permission?: {0}", has_internet_permission)
118+
119+
has_wifi_permission = has_element_attr(manifest_xml,
120+
"uses-permission", "android:name", "android.permission.ACCESS_WIFI_STATE")
121+
Log("has wifi permission?: {0}", has_wifi_permission)
122+
123+
return {"manifest_xml" : manifest_xml,
124+
"has_adjust_receiver" : has_adjust_receiver,
125+
"has_internet_permission" : has_internet_permission,
126+
"has_wifi_permission" : has_wifi_permission}
127+
128+
def has_element_attr(xml_dom, tag_name, attr_name, attr_value):
129+
for node in xml_dom.getElementsByTagName(tag_name):
130+
attr_dom = node.getAttribute(attr_name)
131+
if attr_dom == attr_value:
132+
return True
133+
return False
134+
135+
def copy_adjust_manifest(Log, android_plugin_path):
136+
adjust_manifest_path = os.path.join(android_plugin_path, "AdjustAndroidManifest.xml")
137+
new_manifest_path = os.path.join(android_plugin_path, "AndroidManifest.xml")
138+
try:
139+
shutil.copyfile(adjust_manifest_path, new_manifest_path)
140+
except Exception as e:
141+
Log(e)
142+
else:
143+
Log("Manifest copied from {0} to {1}",
144+
adjust_manifest_path, new_manifest_path)
145+
146+
def LogInput(writeObject):
147+
def Log(message, *args):
148+
messageNLine = str(message) + "\n"
149+
writeObject.write(messageNLine.format(*args))
150+
return Log
151+
152+
def parse_input(Log, parser):
153+
args = parser.parse_args()
154+
assets_path = args.assets_path
155+
156+
android_plugin_path = os.path.join(assets_path, "Plugins/Android/")
157+
Log("Android plugin path: {0}", android_plugin_path)
158+
159+
return android_plugin_path, args.pre_build
160+
161+
if __name__ == "__main__":
162+
main()

Assets/Editor/AdjustPostBuildAndroid.meta

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Editor/AdjustPostBuildiOS

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
import re
5+
from subprocess import Popen, PIPE
6+
import argparse
7+
8+
from mod_pbxproj import XcodeProject
9+
10+
def main():
11+
parser = argparse.ArgumentParser(description="Adjust post build iOS script")
12+
parser.add_argument('ios_project_path', help="path to the folder of the iOS project generated by unity3d")
13+
14+
with open('AdjustPostBuildiOSLog.txt','w') as fileLog:
15+
# log function with file injected
16+
LogFunc = LogInput(fileLog)
17+
18+
# path of the Xcode SDK on the system
19+
xcode_sdk_path = get_xcode_sdk_path(LogFunc)
20+
21+
# path for unity iOS Xcode project and adFramework on the system
22+
unity_xcode_project_path, adFramework_path = get_paths(LogFunc, parser, xcode_sdk_path)
23+
24+
# edit the Xcode project using mod_pbxproj
25+
# add the adFramework library
26+
# change the compilation flags of the adjust project files to support non-ARC
27+
edit_unity_xcode_project(LogFunc, unity_xcode_project_path, adFramework_path)
28+
29+
# change the Xcode project directly
30+
# allow objective-c exceptions
31+
rewrite_unity_xcode_project(LogFunc, unity_xcode_project_path)
32+
sys.exit(0)
33+
34+
def LogInput(writeObject):
35+
def Log(message, *args):
36+
messageNLine = (message if message else "None") + "\n"
37+
writeObject.write(messageNLine.format(*args))
38+
return Log
39+
40+
def get_paths(Log, parser, xcode_sdk_path):
41+
args = parser.parse_args()
42+
ios_project_path = args.ios_project_path
43+
44+
unity_xcode_project_path = ios_project_path + "/Unity-iPhone.xcodeproj/project.pbxproj"
45+
Log("Unity3d Xcode project path: {0}", unity_xcode_project_path)
46+
47+
adFramework_path = xcode_sdk_path + "/System/Library/Frameworks/AdSupport.framework"
48+
Log("adFramework path: {0}", adFramework_path)
49+
50+
return unity_xcode_project_path, adFramework_path
51+
52+
def edit_unity_xcode_project(Log, unity_xcode_project_path, adFramework_path):
53+
# load unity iOS pbxproj project file
54+
unity_XcodeProject = XcodeProject.Load(unity_xcode_project_path)
55+
56+
# add adFramework to unity
57+
unity_XcodeProject.add_file_if_doesnt_exist(adFramework_path, tree="SDKROOT", create_build_files=True,weak=True)
58+
Log("added adSupport framework")
59+
60+
# regex for adjust sdk files
61+
re_adjust_files = re.compile(r"AI.*\.m|.*\+AI.*\.m|Adjust\.m|AdjustUnity\.mm")
62+
63+
# iterate all objects in the unity Xcode iOS project file
64+
for key in unity_XcodeProject.get_ids():
65+
obj = unity_XcodeProject.get_obj(key)
66+
67+
name = obj.get('name')
68+
isa = obj.get('isa')
69+
path = obj.get('path')
70+
fileref = obj.get('fileRef')
71+
72+
#Log("key: {0}, name: {1}, isa: {2}, path: {3}, fileref: {4}", key, name, isa, path, fileref)
73+
74+
#check if file reference match any adjust file
75+
adjust_file_match = re_adjust_files.match(name if name else "")
76+
if (adjust_file_match):
77+
#Log("file match, group: {0}", adjust_file_match.group())
78+
# get the build file, from the file reference id
79+
build_files = unity_XcodeProject.get_build_files(key)
80+
for build_file in build_files:
81+
# add the ARC compiler flag to the adjust file if doesn't exist
82+
build_file.add_compiler_flag('-fobjc-arc')
83+
Log("added ARC flag to file {0}", name)
84+
85+
# save changes
86+
unity_XcodeProject.saveFormat3_2()
87+
88+
def rewrite_unity_xcode_project(Log, unity_xcode_project_path):
89+
unity_xcode_lines = []
90+
# allow objective-c exceptions
91+
re_objc_excep = re.compile(r"\s*GCC_ENABLE_OBJC_EXCEPTIONS *= *NO.*")
92+
with open(unity_xcode_project_path) as upf:
93+
for line in upf:
94+
if re_objc_excep.match(line):
95+
#Log("matched line: {0}", re_objc_excep.match(line).group())
96+
line = line.replace("NO","YES")
97+
Log("Objective-c exceptions enabled")
98+
unity_xcode_lines.append(line)
99+
with open(unity_xcode_project_path, "w+") as upf:
100+
upf.writelines(unity_xcode_lines)
101+
102+
def get_xcode_sdk_path(Log):
103+
# outputs all info from xcode
104+
proc = Popen(["xcodebuild", "-version", "-sdk"], stdout=PIPE, stderr=PIPE)
105+
out, err = proc.communicate()
106+
107+
if proc.returncode not in [0, 66]:
108+
Log("Could not retrieve Xcode sdk path. code: {0}, err: {1}", proc.returncode, err)
109+
return None
110+
111+
match = re.search("iPhoneOS.*?Path: (?P<sdk_path>.*?)\n", out, re.DOTALL)
112+
xcode_sdk_path = match.group('sdk_path') if match else None
113+
Log("Xcode sdk path: {0}", xcode_sdk_path)
114+
return xcode_sdk_path
115+
116+
if __name__ == "__main__":
117+
main()

0 commit comments

Comments
 (0)