Skip to content
This repository has been archived by the owner on Feb 4, 2020. It is now read-only.

Use Xcode as system generator and builder on OSX #127

Merged
merged 8 commits into from
Apr 8, 2017
Merged

Use Xcode as system generator and builder on OSX #127

merged 8 commits into from
Apr 8, 2017

Conversation

esteve
Copy link
Contributor

@esteve esteve commented Dec 8, 2016

This PR replaces make with xcodebuild as the builder for OSX, this among other things allows for ROS2 to be compiled for iOS and the iOS Simulator.

I kept the name of the builder flags option (make-flags), but I think that if this gets merged it may make sense to rename it to something else (e.g. builder-flags), since Linux would be the only platform to actually use make

@esteve esteve added in progress Actively being worked on (Kanban column) in review Waiting for review (Kanban column) and removed in progress Actively being worked on (Kanban column) labels Dec 8, 2016
@esteve esteve requested a review from wjwwood March 22, 2017 11:21
@esteve
Copy link
Contributor Author

esteve commented Mar 22, 2017

@wjwwood sorry for requesting a review from you specifically, but given that you're the one who uses OSX to work on ROS2, I thought you'd the most appropriate for checking out this. However, if anyone else on the @ros2/team feels like having a look at the changes, please feel free to do so.

I believe the changes in this PR can be merged because they:

  • do not break existing code and all the existing usecases are supported (e.g. parallel building). IIRC last time I ran this on local, everything worked fine, hopefully nothing has changed.
  • add support for using ROS 2.0 as an XCode project (e.g. debugging from the IDE)
  • add support for building ROS 2.0 on iOS. The iOS SDK does not support Make AFAIK.

@wjwwood
Copy link
Contributor

wjwwood commented Mar 24, 2017

@esteve the changes look ok to me, but I would like to preserve the capability to build with make on macOS, something like a --use-xcode option for ament build. I'll try to take a stab at implementing this as an option as soon as I can.

@wjwwood
Copy link
Contributor

wjwwood commented Mar 24, 2017

Just for some of my thinking behind that desire... Xcode is not currently a requirement for building ROS 2 because you can just install "Command Line Tools" and avoid Xcode, which requires an apple account (I think that's still the case anyways). Also, I like to go into my build folders and run make ... 😄.

But still, the ability to run in Xcode is pretty nice. Luckily I don't think it will be difficult to make this switch optional.

@esteve
Copy link
Contributor Author

esteve commented Mar 24, 2017

@wjwwood probably bad phrasing on my part in my previous comment, but Xcode is not really a requirement for this. xcodebuild, which replaces make on this PR, is part of the "Command Line Tools" as well, so when you install Homebrew (which is a requirement for ROS 2.0), you already have it. So you can still build from the command line using either xcodebuild or cmake --build. I don't know if I still have permissions to trigger a CI build, but this change should still work on the buildfarm, which does not have Xcode installed.

Another option, if you want to keep the Make-based system is to check what generator was used (CMake's -G flag), and let ament choose make or xcodebuild accordingly.

@esteve
Copy link
Contributor Author

esteve commented Mar 24, 2017

@wjwwood what I mean is that, analogously to this code:

https://github.com/esteve/ament_tools/blob/xcode/ament_tools/build_types/cmake.py#L410-L435

something similar could be done in the build phase:

        elif IS_MACOSX:
            # Check CMake was invoked to generate a Xcode or Make project
            # The Xcode CMake generator will produce a file named
            # ALL_BUILD_cmakeRulesBuildPhase.makeRelease if the Xcode generator
            # was used, whether the build type is Release, Debug or anything else
            all_build_cmake_file_path = os.path.join(
                context.build_space, 'CMakeScripts', 'ALL_BUILD_cmakeRulesBuildPhase.makeRelease')
            all_build_cmake_file = os.path.isfile(all_build_cmake_file_path)
            if all_build_cmake_file:
                # This is an Xcode project, use xcodebuild to compile
                ...
                yield BuildAction(prefix + [XCODEBUILD_EXECUTABLE] + xcodebuild_args)
            else:
                # Assume that CMake generated a Make-based project
                ...
                yield BuildAction(prefix + [MAKE_EXECUTABLE] + context.make_flags)

and default to the Make generator. IMHO xcodebuild is better than Make (anything is better than Make 😄 ) and both are abstracted by cmake --build, but it's true that people are used to Make and it's hard to change habits, so if it causes less friction, I'm fine to set Make as the default type for the CMake generator.

What do you think? I can write that logic, but it'd be good to run a CI build with Xcode as the generator as well.

@wjwwood
Copy link
Contributor

wjwwood commented Mar 24, 2017

I don't actually mind what the default is on macOS (as long as it comes with CLT and doesn't introduce regressions), but I do think there should be a way to use make if you want, whether or not that's the default.

@esteve
Copy link
Contributor Author

esteve commented Mar 29, 2017

@wjwwood I've updated the PR to support both Make and Xcode, I've left Xcode as the default, but it can easily be changed to Make here:

https://github.com/esteve/ament_tools/blob/xcode/ament_tools/build_types/cmake.py#L171-L172

to override the default, the user can pass the -G flag to --cmake-args or --ament-cmake-args.

However, I had always tested with PR without symlink-install and I just noticed that it won't work because the Xcode generator is a multi-config generator (https://cmake.org/cmake/help/v3.5/variable/CMAKE_BUILD_TYPE.html#variable:CMAKE_BUILD_TYPE and https://cmake.org/cmake/help/v3.5/variable/CMAKE_CONFIGURATION_TYPES.html), and thus creates multiple conflicting targets if ament is asked to create symbolic links for the install step. I ended up adding the following to ament_cmake esteve/ament_cmake@9c27a52, so I don't know if it's just better to keep Make as the default generator/builder for CMake.

@dirk-thomas
Copy link
Contributor

With the downside of not being able to use the symlink installs (not sure if that could be fixed, I don't have a platform with multi-config to test this on) I would propose to keep make the default.

to override the default, the user can pass the -G flag to --cmake-args or --ament-cmake-args.

I would suggest adding a option --use-xcode as it is done in ROS 1 for other generators already.

Please see #144 for a different PR adding support for Ninja which will likely interfere with this.

@esteve
Copy link
Contributor Author

esteve commented Mar 30, 2017

@dirk-thomas I agree, it makes sense. I've made the Make generator the default again (and more explicit see https://github.com/esteve/ament_tools/blob/xcode/ament_tools/build_types/cmake.py#L181) and added a --use-xcode option to ament which is only available when run on macOS.

Since it's not the default, I also added -DCMAKE_XCODE_GENERATE_SCHEME=ON, which will be available in CMake 3.8.0. The new scheme generator for CMake 3.8 adds better support for xcodebuild test, for example. This way, the default behavior will still be compatible with the minimum requirements (i.e. CMake 3.5) and only the optional --use-xcode flag requires a greater version of CMake.

As for the issue with multi-config generators and symlink installs, should I submit a PR with the change in esteve/ament_cmake@9c27a52 ?

@dirk-thomas
Copy link
Contributor

As for the issue with multi-config generators and symlink installs, should I submit a PR with the change in esteve/ament_cmake@9c27a52 ?

It would be good to separate that change. I don't see why the symlinks install should need to be disabled. It is supposed to replace the normal install rules transparently - and those obviously work for multi configuration generators. So it looks like the symlinks install stuff needs to be modified somehow to work in that scenario.

@esteve
Copy link
Contributor Author

esteve commented Mar 30, 2017

and those obviously work for multi configuration generators.

@dirk-thomas this issue hadn't show up yet because the only multi config generator that's in use in ROS 2.0 is Visual Studio's and symlinks are not supported in Windows. However, macOS supports symlinks but the Xcode generator fails with the code here:

https://github.com/ament/ament_cmake/blob/master/ament_cmake_core/cmake/symlink_install/ament_cmake_symlink_install_targets.cmake#L96-L98

invoking ament with src/ament/ament_tools/scripts/ament.py build --use-xcode --build-tests --symlink-install --isolated --build-space build_isolated_xcode --install-space install_isolated_xcode produces the following error:

+++ Building 'class_loader'
==> '. /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/cmake__build.sh && /usr/local/bin/cmake /Users/esteve/ros2_ws/src/ros/class_loader -DBUILD_TESTING=1 -DAMENT_CMAKE_SYMLINK_INSTALL=1 -DCMAKE_INSTALL_PREFIX=/Users/esteve/ros2_ws/install_isolated_xcode/class_loader -G Xcode -DCMAKE_XCODE_GENERATE_SCHEME=ON' in '/Users/esteve/ros2_ws/build_isolated_xcode/class_loader'
-- Found ament_cmake: 0.0.0 (/Users/esteve/ros2_ws/install_isolated_xcode/ament_cmake/share/ament_cmake/cmake)
-- Using PYTHON_EXECUTABLE: /usr/local/bin/python3
-- Override CMake install command with custom implementation using symlinks instead of copying resources
-- Found poco_vendor: 1.0.0 (/Users/esteve/ros2_ws/install_isolated_xcode/poco_vendor/share/poco_vendor/cmake)
-- Searching for Poco library...
--   Found Poco!
-- components found: Foundation.
-- Configuring done
CMake Error in CMakeLists.txt:
  Evaluation file to be written multiple times for different configurations
  or languages with different content:

    /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/ament_cmake_symlink_install_targets_0.cmake


CMake Error in CMakeLists.txt:
  Evaluation file to be written multiple times for different configurations
  or languages with different content:

    /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/ament_cmake_symlink_install_targets_0.cmake


CMake Error in CMakeLists.txt:
  Evaluation file to be written multiple times for different configurations
  or languages with different content:

    /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/ament_cmake_symlink_install_targets_0.cmake


CMake Error in CMakeLists.txt:
  Evaluation file to be written multiple times for different configurations
  or languages with different content:

    /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/ament_cmake_symlink_install_targets_0.cmake


CMake Error in CMakeLists.txt:
  Evaluation file to be written multiple times for different configurations
  or languages with different content:

    /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/ament_cmake_symlink_install_targets_0.cmake


CMake Error in CMakeLists.txt:
  Evaluation file to be written multiple times for different configurations
  or languages with different content:

    /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/ament_cmake_symlink_install_targets_0.cmake


-- Generating done
-- Build files have been written to: /Users/esteve/ros2_ws/build_isolated_xcode/class_loader

<== Command '. /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/cmake__build.sh && /usr/local/bin/cmake /Users/esteve/ros2_ws/src/ros/class_loader -DBUILD_TESTING=1 -DAMENT_CMAKE_SYMLINK_INSTALL=1 -DCMAKE_INSTALL_PREFIX=/Users/esteve/ros2_ws/install_isolated_xcode/class_loader -G Xcode -DCMAKE_XCODE_GENERATE_SCHEME=ON' failed in '/Users/esteve/ros2_ws/build_isolated_xcode/class_loader' with exit code '1'
<== Command '. /Users/esteve/ros2_ws/build_isolated_xcode/class_loader/cmake__build.sh && /usr/local/bin/cmake /Users/esteve/ros2_ws/src/ros/class_loader -DBUILD_TESTING=1 -DAMENT_CMAKE_SYMLINK_INSTALL=1 -DCMAKE_INSTALL_PREFIX=/Users/esteve/ros2_ws/install_isolated_xcode/class_loader -G Xcode -DCMAKE_XCODE_GENERATE_SCHEME=ON' failed in '/Users/esteve/ros2_ws/build_isolated_xcode/class_loader' with exit code '1'

elif IS_MACOSX:
if self._using_xcode_generator(context):
if XCODEBUILD_EXECUTABLE is None:
raise VerbExecutionError("Could not find 'xcodebuild' executable")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this prevent building with Makefiles when xcodebuild is not available?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just based on the code, I think this only happens when you've specified that you want to use the xcode generator.

flag.replace(
'-j', '-IDEBuildOperationMaxNumberOfConcurrentCompileTasks='))
else:
xcodebuild_flags.append(flag)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic is a bit difficult to follow. Shouldn't this be the equivalent of:

if flag.startswith('-j'):
    xcodebuild_flags.append(flag.replace(
        '-j', '-IDEBuildOperationMaxNumberOfConcurrentCompileTasks='))
elif not flag.startswith('-l'):
    xcodebuild_flags.append(flag)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍, the altered logic is easier to read I think.

xcodebuild_flags.append(flag)
xcodebuild_flags += ['-configuration']
xcodebuild_flags += [self._get_configuration_from_cmake(context)]
cmd.extend(xcodebuild_flags)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost the logic has been added above. This should be deduplicated.

Another time below.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that some of this logic could be consolidated with the code above. Especially the handling of the context.make_flags.

Copy link
Contributor Author

@esteve esteve Apr 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I've added @dirk-thomas 's code and moved it to a separate function in 9ace6e8

Thanks!

Update: I removed the duplicated code since it was not needed and simplified the logic for flag processing in 1974198

Copy link
Contributor

@wjwwood wjwwood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really close I think. There were a few requests to simplify logic and consolidate code. Once those are done and we test/merge @dirk-thomas change to support multiple build types with symlink install, then I'll spend some time testing this out locally on my own machine.

Thanks @esteve for iterating with us.

elif IS_MACOSX:
if context.use_xcode or self._using_xcode_generator(context):
cmake_args += ['-G', 'Xcode']
cmake_args += ['-DCMAKE_XCODE_GENERATE_SCHEME=ON']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one assumes CMake 3.8.0 right? Could we conditionally add that with that version only? Or is there no point in running without it? My impression was that it just improved testing with Xcode?

Copy link
Contributor Author

@esteve esteve Apr 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the CMAKE_XCODE_GENERATE_SCHEME flag and used the RUN_TESTS target instead for ament test as a workaround in eaf9430 This approach is compatible with CMake 3.5, so no need to use anything experimental from 3.8

Update: the latest commit id with the change is a39cd8c

flag.replace(
'-j', '-IDEBuildOperationMaxNumberOfConcurrentCompileTasks='))
else:
xcodebuild_flags.append(flag)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍, the altered logic is easier to read I think.

xcodebuild_flags.append(flag)
xcodebuild_flags += ['-configuration']
xcodebuild_flags += [self._get_configuration_from_cmake(context)]
cmd.extend(xcodebuild_flags)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that some of this logic could be consolidated with the code above. Especially the handling of the context.make_flags.

@mikaelarguedas mikaelarguedas added in progress Actively being worked on (Kanban column) and removed in review Waiting for review (Kanban column) labels Apr 5, 2017
@esteve
Copy link
Contributor Author

esteve commented Apr 7, 2017

@dirk-thomas @wjwwood I've addressed your feedback, let me know if there's anything else that needs to be fixed/improved. Thanks!

@esteve
Copy link
Contributor Author

esteve commented Apr 7, 2017

@dirk-thomas @wjwwood I just realized that the duplicated code was not really needed in the install and uninstall verbs, so I moved the code back to _common_cmake_on_build

@dirk-thomas
Copy link
Contributor

Not directly related to this patch but a lot of this custom logic would be unnecessary if we would implement #88. But since ament_tools is likely not being developed much further (see http://design.ros2.org/articles/build_tool.html) I don't think we will spend time to migrate to cmake --build. Since this patch addresses the short term desire to use XCode that's fine with me.

I will leave it up to @wjwwood to try it on OS X and comment / merge this patch.

@wjwwood
Copy link
Contributor

wjwwood commented Apr 7, 2017

I tested the --use-xcode option locally and it works!

I also ran CI with the default behavior to see if breaks anything, but it works (after 0371b58):

But of course just as I get it tested, it's all broken now that @dirk-thomas pushed #144 through.

I'll have a look at the conflict resolution...

@wjwwood
Copy link
Contributor

wjwwood commented Apr 8, 2017

Passing CI:

  • Linux:
    • Build Status
  • macOS:
    • Build Status (with some flaky tests)
  • Windows:
    • Build Status (also with some flaky tests)

@wjwwood wjwwood merged commit 2ed6995 into ament:master Apr 8, 2017
@wjwwood wjwwood removed the in progress Actively being worked on (Kanban column) label Apr 8, 2017
@wjwwood
Copy link
Contributor

wjwwood commented Apr 8, 2017

Thanks again @esteve!

@esteve esteve deleted the xcode branch April 10, 2017 10:28
@esteve
Copy link
Contributor Author

esteve commented Apr 10, 2017

Yay! Thanks @wjwwood for testing this and for fixing the conflicts.

@esteve
Copy link
Contributor Author

esteve commented Apr 10, 2017

And thanks @dirk-thomas for the review.

dhood added a commit that referenced this pull request May 1, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants