nitc ios: xcode support per file cflags
[nit.git] / src / platform / ios.nit
1 # This file is part of NIT ( http://www.nitlanguage.org )
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Compile programs for the iOS platform
16 module ios
17
18 import platform
19 import compiler::abstract_compiler
20 import xcode_templates
21 import app_annotations
22
23 redef class ToolContext
24 redef fun platform_from_name(name)
25 do
26 if name == "ios" then return new IOSPlatform
27 return super
28 end
29 end
30
31 private class IOSPlatform
32 super Platform
33
34 redef fun supports_libunwind do return false
35 redef fun supports_libgc do return false
36 redef fun toolchain(toolcontext, compiler) do return new IOSToolchain(toolcontext, compiler)
37 end
38
39 private class IosProject
40 super AppProject
41
42 redef fun namespace do return super.to_camel_case
43 end
44
45 private class IOSToolchain
46 super MakefileToolchain
47
48 # Root of the iOS project, usually `nit_compile/ios/`
49 var ios_project_root: String is noinit
50
51 # `app.nit` project for the current compilation target
52 var app_project = new IosProject(compiler.modelbuilder, compiler.mainmodule) is lazy
53
54 redef fun default_outname do return "{super}.app"
55
56 # Compile C files in `ios_project_root/app_project.name`
57 redef fun compile_dir
58 do
59 ios_project_root = root_compile_dir/"ios"
60 return ios_project_root/app_project.short_name
61 end
62
63 redef fun write_files(compile_dir, cfiles)
64 do
65 # Clear the project directory before writing anything
66 if ios_project_root.file_exists then ios_project_root.rmdir
67 compile_dir.mkdir
68
69 super
70 end
71
72 redef fun write_makefile(compile_dir, cfiles)
73 do
74 var project_name = app_project.short_name
75
76 # ---
77 # project_folder (source code)
78
79 # Create the plist in the same directory as the generated C code
80 if not compile_dir.file_exists then compile_dir.mkdir
81 var plist = new PlistTemplate(app_project.name, app_project.namespace,
82 app_project.version, app_project.version_code.to_s)
83 plist.write_to_file compile_dir/"Info.plist"
84
85 # Copy the folder `ios/AppIcon.appiconset` from the root of the project
86 var project_root = "."
87 var mpackage = compiler.mainmodule.first_real_mmodule.mpackage
88 if mpackage != null then
89 var root = mpackage.root
90 if root != null then
91 var filepath = root.filepath
92 if filepath != null then
93 project_root = filepath
94 end
95 end
96 end
97
98 # Copy all resources
99 var app_files = [project_root]
100 app_files.add_all app_project.files
101
102 var icons_found = false
103
104 # Prepare the `Assets.xcassets` folder
105 var target_assets_dir = compile_dir / "Assets.xcassets"
106 if not target_assets_dir.file_exists then target_assets_dir.mkdir
107 """
108 {
109 "info" : {
110 "version" : 1,
111 "author" : "nitc"
112 }
113 }""".write_to_file target_assets_dir / "Contents.json"
114
115 (compile_dir / "assets").mkdir
116
117 for path in app_files do
118
119 # Icon
120 var icon_dir = path / "ios" / "AppIcon.appiconset"
121 if icon_dir.file_exists then
122 icons_found = true
123
124
125 # copy the res folder to the compile dir
126 icon_dir = icon_dir.realpath
127 toolcontext.exec_and_check(["cp", "-R", icon_dir, target_assets_dir], "iOS project error")
128 end
129
130 # Assets
131 var assets_dir = path / "assets"
132 if assets_dir.file_exists then
133 assets_dir = assets_dir.realpath
134 toolcontext.exec_and_check(["cp", "-r", assets_dir, compile_dir], "iOS project error")
135 end
136 end
137
138 # ---
139 # project_folder.xcodeproj (projet meta data)
140
141 # Create an XCode project directory
142 var dir = ios_project_root/project_name+".xcodeproj"
143 if not dir.file_exists then dir.mkdir
144
145 # Create a PBX project file
146 var pbx = new PbxprojectTemplate(project_name)
147
148 ## Register all source files
149 for file in cfiles do pbx.add_file new PbxFile(file)
150 for file in compiler.extern_bodies do
151 pbx.add_file new PbxFile(file.filename.basename)
152 end
153
154 # Basic storyboard, mainly to have the right screen size
155 var launch_screen_storyboard = new LaunchScreenStoryboardTemplate
156 launch_screen_storyboard.title = app_project.name
157 launch_screen_storyboard.subtitle = "app.nit"
158 launch_screen_storyboard.write_to_file ios_project_root / "LaunchScreen.storyboard"
159
160 # Register the Assets.xcassets folder in the project description
161 if icons_found then
162 var xcassets = new PbxFile("Assets.xcassets")
163 pbx.add_file xcassets
164 end
165
166 pbx.write_to_file dir / "project.pbxproj"
167 end
168
169 redef fun compile_c_code(compile_dir)
170 do
171 var project_name = app_project.short_name
172 var release = toolcontext.opt_release.value
173 var outfile = outfile(compiler.mainmodule)
174
175 # Compile with `xcodebuild`
176 #
177 # TODO support more than the iPhone and the simulator.
178 var compile_mode = if release then "Release" else "Debug"
179 var args = ["sh", "-c", "cd {ios_project_root}; " +
180 "xcodebuild -quiet -target '{project_name}' " +
181 "-destination 'platform=iOS Simulator,name=iPhone' " +
182 "-configuration {compile_mode} " +
183 "ONLY_ACTIVE_ARCH=NO "+
184 "-sdk iphonesimulator build"]
185 toolcontext.exec_and_check(args, "iOS project error")
186
187 # Move compiled app to destination
188 if outfile.file_exists then
189 var error = outfile.rmdir
190 if error != null then
191 print_error error
192 exit 1
193 end
194 end
195
196 args = ["mv", "{ios_project_root}/build/{compile_mode}-iphonesimulator/{project_name}.app", outfile]
197 toolcontext.exec_and_check(args, "iOS project error")
198 end
199 end