Difference between revisions of "Xcode"
m |
m (→simctl) |
||
(24 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
The Apple's default IDE is quite hostile to FPC, since it doesn't provide any source of "native" integration. In order for the compiler to be called a custom [https://developer.apple.com/library/mac/recipes/xcode_help-project_editor/Articles/AddingaRunScriptBuildPhase.html '''Run Script'''] is needed to be used. | The Apple's default IDE is quite hostile to FPC, since it doesn't provide any source of "native" integration. In order for the compiler to be called a custom [https://developer.apple.com/library/mac/recipes/xcode_help-project_editor/Articles/AddingaRunScriptBuildPhase.html '''Run Script'''] is needed to be used. | ||
− | However it comes with some handy command-line tools that could be used for building projects. See | + | However it comes with some handy command-line tools that could be used for building projects. See [https://developer.apple.com/library/ios/technotes/tn2339/_index.html Technical Note TN2339] for FAQ. |
− | ==xcodebuild== | + | ==Command Line Utilities== |
+ | ===xcodebuild=== | ||
+ | [https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html xcodebuild Man Page] | ||
List all available SDKs | List all available SDKs | ||
xcodebuild -showsdks | xcodebuild -showsdks | ||
Build a project by it's (project directory) name and a specified sdk | Build a project by it's (project directory) name and a specified sdk | ||
− | xcodebuild -sdk iphonesimulator9.1 -project testProjGen.xcodeproj | + | xcodebuild -sdk iphonesimulator9.1 -project testProjGen.xcodeproj |
+ | Installs (for iOS Device) or prepares the project for installation (for iPhone Simulator) | ||
+ | xcodebuild install -sdk iphonesimulator9.1 -project testProjGen.xcodeproj | ||
+ | It doesn't install any actual updates to iPhone Simulator, but prepares the resulting bundle for it. It is physically installed by using [[Xcode#simctl|simctl]] tool | ||
+ | |||
+ | ===instruments=== | ||
+ | The utility should only be ran via xcrun. Read manual page provided with Xcode command-line utilities installed. | ||
+ | |||
+ | The following command line runs Simulator booting the specified device. If the simulator is already running it would be rebooted with a the specified device, if it's different to the one already running. | ||
+ | xcrun instruments -w "%identifier%" -t "%template%" | ||
+ | *%identifier% - is a part of the device name or ID. | ||
+ | *%template% - if template is specified, then instruments doesn't return. If template is not specified (causes a warning on running) the instruments exists, leaving the device running. | ||
+ | |||
+ | ===simctl=== | ||
+ | An utility fo additional control over the simulator. Must be ran via "xcrun" | ||
+ | |||
+ | Lists all available devices in JSON format. | ||
+ | xcrun simctl list devices -j | ||
+ | Running without "devices" would list all devices. "-j" requests JSON format | ||
+ | |||
+ | |||
+ | Installing a bundle to the device. The bundle could be either generated manually or prepared via Xcode (xcodebuild) | ||
+ | xcrun simctl install booted ~/project1/bundle.app | ||
+ | Note, that once the bundle is installed it's referenced by the application ID, that's defined at info.plist file within the bundle. | ||
+ | |||
+ | |||
+ | Running an application on a booted device. | ||
+ | xcrun simctrl launch booted bundle.mycompany.id | ||
+ | "booted" can be replaced with an actual device id. | ||
+ | |||
+ | |||
+ | Running an application with waiting for a debugger (lldb). | ||
+ | xcrun simctl launch -w booted bundle.mycompany.id | ||
+ | The booted application will launch and pid for the application will return. For example: | ||
+ | bundle.id 1200 | ||
+ | |||
+ | ===lldb=== | ||
+ | lldb has replaced gdb as a Apple main debugger utility. Unlike one might expect, it's NOT specific for LLVM or any llvm written program. It can as a "normal" debugger and debug non-LLVM based programs. | ||
+ | |||
+ | While debugger can launch a process itself, it's not applicable for using Simulator application. Since Instruments service typically runs the application first (with an option to wait for the debugger to attach to it). Thus the most important part of the debugger is being able to attach to the process. | ||
+ | |||
+ | The pid is a normal process within OSX environment and should be attached via '''lldb''' debugger, i.e. | ||
+ | lldb attach %pid% | ||
+ | ====Capturing Console Output==== | ||
+ | According to Apple any NSLog() call eventually ends up int "stderr". Since it's desired to see the output in Lazarus, it's necessary to intercept the stderr. On of the convenient ways is to redirect the output to a file. Once it's done the debugger could be released. | ||
+ | |||
+ | The way to redirect the stdout is to reassign unix file-descriptor within the debugged process to a file on the disk. | ||
+ | For this close() function should be used. A file (descriptor) could be via open() function and reassigned to stderr via dup2(). | ||
+ | |||
+ | LLDB allows to run commands within the debugged process by a number of commands (call, p, po). Here's an example of reassigning stderr to a file: | ||
+ | p (int) dup2 ( (int) open("/Users/dmitry/err.txt",2), 2) | ||
+ | Notes: | ||
+ | :open("/Users/dmitry/err.txt",2) - opens the specified file with O_RDWR read/write flag | ||
+ | :dup2( xx, 2) - copied the file descriptor "xx" (that has been open()-ed) to description "2", which is stderr. "1" is stdout) | ||
+ | :each function has an explicit type-case before its usage (int). This is required for debugger without enough debug information provided. If debug info were present the call could look like this: | ||
+ | p dup2 ( open("/Users/dmitry/err.txt",2), 2) | ||
+ | For any non system unix function LLDB HAS to have the debug info available. DWARF format is preferred. | ||
+ | |||
+ | ====Main Break==== | ||
+ | Keep in mind, that the standard output/error must be overwritten as soon as possible. The best place for that is at the beginning of main(). (Note that main() is not the same as pascal main program body at begin..end. Pascal RTL and units does initialization at the main() prior to passing the code to the main program body). | ||
+ | |||
+ | A break point must be set for main() | ||
+ | b main | ||
+ | Once the breakpoint is reached (which should eventually, even if LLDB complains about missing symbolic references to main) | ||
+ | The handlers could be overwritten. | ||
+ | |||
+ | ===Pseudo Terminals=== | ||
+ | One of the problems of using files or even named pipes (fifo) for the reading std-out and std-err, is the fact Pascal RTL is using buffered output for files by default. It might looked like a delayed (or even missing) output. | ||
+ | |||
+ | While it's possible to disable the buffering, by changing the code in the debugged program, in general it is undesired. | ||
+ | |||
+ | Pascal RTL does much less buffering for console output (i.e. each WriteLn call is flushed). | ||
+ | |||
+ | So instead of using files, it's better to use Pseudo Terminals (ptys), which are created via [https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/openpty.3.html openpty] function. | ||
+ | uses | ||
+ | .. Unix, termios ... | ||
+ | |||
+ | type | ||
+ | Ptermios = ^termios; // declared at termios | ||
+ | Pwinsize = ^winsize; | ||
+ | |||
+ | function openpty(amaster:pcint; aslave:pcint; | ||
+ | name:Pchar; termp:Ptermios; winp:Pwinsize):longint;cdecl; | ||
+ | external clib // declared at Unix | ||
+ | name 'openpty'; | ||
+ | The IDE application would create the console, pass the (slave) name to the debugger. | ||
+ | |||
+ | Debugger stops at main() and reassigns the process output to console. | ||
+ | |||
+ | Later IDE reads the output. | ||
==Run Script== | ==Run Script== | ||
* in order to return an error, the (bash) script must use "exit" command with non-zero value. FPC returns a non-zero value when compiling failed. | * in order to return an error, the (bash) script must use "exit" command with non-zero value. FPC returns a non-zero value when compiling failed. | ||
* do not specify "output files" of the script. Otherwise "Clean" will be required to make the script executed again | * do not specify "output files" of the script. Otherwise "Clean" will be required to make the script executed again | ||
+ | ==Debugging== | ||
+ | [https://developer.apple.com/library/ios/technotes/tn2239/_index.html Technical Note TN2239 - iOS Debugging Magic] | ||
+ | ==Xcode project== | ||
+ | ===Executable=== | ||
+ | Name of the '''executable''' is specified at buildSettings option '''PRODUCT_NAME'''. | ||
+ | |||
+ | Executable name should be specified at '''info.plist''' '''CFBundleExecutable''' | ||
+ | |||
+ | Name of the bundle would match the PRODUCT_NAME. | ||
[[Category:Xcode]] | [[Category:Xcode]] |
Latest revision as of 14:15, 1 January 2016
The Apple's default IDE is quite hostile to FPC, since it doesn't provide any source of "native" integration. In order for the compiler to be called a custom Run Script is needed to be used.
However it comes with some handy command-line tools that could be used for building projects. See Technical Note TN2339 for FAQ.
Contents
Command Line Utilities
xcodebuild
xcodebuild Man Page List all available SDKs
xcodebuild -showsdks
Build a project by it's (project directory) name and a specified sdk
xcodebuild -sdk iphonesimulator9.1 -project testProjGen.xcodeproj
Installs (for iOS Device) or prepares the project for installation (for iPhone Simulator)
xcodebuild install -sdk iphonesimulator9.1 -project testProjGen.xcodeproj
It doesn't install any actual updates to iPhone Simulator, but prepares the resulting bundle for it. It is physically installed by using simctl tool
instruments
The utility should only be ran via xcrun. Read manual page provided with Xcode command-line utilities installed.
The following command line runs Simulator booting the specified device. If the simulator is already running it would be rebooted with a the specified device, if it's different to the one already running.
xcrun instruments -w "%identifier%" -t "%template%"
- %identifier% - is a part of the device name or ID.
- %template% - if template is specified, then instruments doesn't return. If template is not specified (causes a warning on running) the instruments exists, leaving the device running.
simctl
An utility fo additional control over the simulator. Must be ran via "xcrun"
Lists all available devices in JSON format.
xcrun simctl list devices -j
Running without "devices" would list all devices. "-j" requests JSON format
Installing a bundle to the device. The bundle could be either generated manually or prepared via Xcode (xcodebuild)
xcrun simctl install booted ~/project1/bundle.app
Note, that once the bundle is installed it's referenced by the application ID, that's defined at info.plist file within the bundle.
Running an application on a booted device.
xcrun simctrl launch booted bundle.mycompany.id
"booted" can be replaced with an actual device id.
Running an application with waiting for a debugger (lldb).
xcrun simctl launch -w booted bundle.mycompany.id
The booted application will launch and pid for the application will return. For example:
bundle.id 1200
lldb
lldb has replaced gdb as a Apple main debugger utility. Unlike one might expect, it's NOT specific for LLVM or any llvm written program. It can as a "normal" debugger and debug non-LLVM based programs.
While debugger can launch a process itself, it's not applicable for using Simulator application. Since Instruments service typically runs the application first (with an option to wait for the debugger to attach to it). Thus the most important part of the debugger is being able to attach to the process.
The pid is a normal process within OSX environment and should be attached via lldb debugger, i.e.
lldb attach %pid%
Capturing Console Output
According to Apple any NSLog() call eventually ends up int "stderr". Since it's desired to see the output in Lazarus, it's necessary to intercept the stderr. On of the convenient ways is to redirect the output to a file. Once it's done the debugger could be released.
The way to redirect the stdout is to reassign unix file-descriptor within the debugged process to a file on the disk. For this close() function should be used. A file (descriptor) could be via open() function and reassigned to stderr via dup2().
LLDB allows to run commands within the debugged process by a number of commands (call, p, po). Here's an example of reassigning stderr to a file:
p (int) dup2 ( (int) open("/Users/dmitry/err.txt",2), 2)
Notes:
- open("/Users/dmitry/err.txt",2) - opens the specified file with O_RDWR read/write flag
- dup2( xx, 2) - copied the file descriptor "xx" (that has been open()-ed) to description "2", which is stderr. "1" is stdout)
- each function has an explicit type-case before its usage (int). This is required for debugger without enough debug information provided. If debug info were present the call could look like this:
p dup2 ( open("/Users/dmitry/err.txt",2), 2)
For any non system unix function LLDB HAS to have the debug info available. DWARF format is preferred.
Main Break
Keep in mind, that the standard output/error must be overwritten as soon as possible. The best place for that is at the beginning of main(). (Note that main() is not the same as pascal main program body at begin..end. Pascal RTL and units does initialization at the main() prior to passing the code to the main program body).
A break point must be set for main()
b main
Once the breakpoint is reached (which should eventually, even if LLDB complains about missing symbolic references to main) The handlers could be overwritten.
Pseudo Terminals
One of the problems of using files or even named pipes (fifo) for the reading std-out and std-err, is the fact Pascal RTL is using buffered output for files by default. It might looked like a delayed (or even missing) output.
While it's possible to disable the buffering, by changing the code in the debugged program, in general it is undesired.
Pascal RTL does much less buffering for console output (i.e. each WriteLn call is flushed).
So instead of using files, it's better to use Pseudo Terminals (ptys), which are created via openpty function.
uses .. Unix, termios ... type Ptermios = ^termios; // declared at termios Pwinsize = ^winsize; function openpty(amaster:pcint; aslave:pcint; name:Pchar; termp:Ptermios; winp:Pwinsize):longint;cdecl; external clib // declared at Unix name 'openpty';
The IDE application would create the console, pass the (slave) name to the debugger.
Debugger stops at main() and reassigns the process output to console.
Later IDE reads the output.
Run Script
- in order to return an error, the (bash) script must use "exit" command with non-zero value. FPC returns a non-zero value when compiling failed.
- do not specify "output files" of the script. Otherwise "Clean" will be required to make the script executed again
Debugging
Technical Note TN2239 - iOS Debugging Magic
Xcode project
Executable
Name of the executable is specified at buildSettings option PRODUCT_NAME.
Executable name should be specified at info.plist CFBundleExecutable
Name of the bundle would match the PRODUCT_NAME.