AnarchyTools / atbuild

The Anarchy Tools build tool
Apache License 2.0
6 stars 1 forks source link

Add Package level `platforms` and `build-configs` items #36

Open owensd opened 8 years ago

owensd commented 8 years ago

All of our software is targeted on platforms and have different build-configs. It might be better to call these out more explicitly from the overlay system.

I'm not 100% sure this is the right thing to do, but it starting to seem more and more like it.

drewcrawford commented 8 years ago

Honestly I think the right thing to do is just activate an overlay called _platform_osx or atbuild.osx or similar if you're building on osx.

Then we need some CLI syntax to deactivate, in the case that the user wants to cross-compile.

owensd commented 8 years ago

Specially named overlays is definitely one approach that could work.

Optionally, we could alias "--platform " on the CLI to also add to the "use-overlays" config.

owensd commented 8 years ago

gah, stupid phone comments...

Specially named overlays is definitely one approach that could work.

Optionally, we could alias "--platform " on the CLI to also add to the "use-overlays" config.

drewcrawford commented 8 years ago

We've had platform support for several releases. I'm happy with it.

I now move to advance a proposal for configurations, which solves the outstanding problem in this issue.

Configurations

Essentially, I propose we replicate the platform model. We have a set of mutually-exclusive, globally-defined configurations:

selection

You are always in some configuration, just like you are always targeting some platform. We pick some default (probably debug, since if you are typing atbuild into a CLI you are probably debugging?). You can override the default with atbuild task --configuration [config].

Implementation

Each tool would be free to interpret the configuration in its own way, but things that poorly support the use case (e.g. Enabling "slow compile" features in debug builds, or "slow performing" features in release builds) would be considered bugs in the tool.

Configuration would be exposed to custom tools in a similar way to platforms, so custom tools can also condition on configuration behavior just like built in tools.

In practice, atllbuild would enable optimizations in release configuration, would emit debug info in debug configuration. It might use configuration as one signal to enable bitcode generation, since bitcode is slow (e.g. Enable bitcode when release && iOS). packageatbin might choose faster compression in debug, and better compression in release. If we get a strip tool #73, behavior might be different in some way per-configuration. But the point would be that we would define "use cases" (like interactive debugging) and the individual tool would try to do something sensible for the use case.

The plain/none/nop configuration is basically a "do nothing", "vanilla", or "no magic" configuration. So here tools would be expected to not assume anything about the use case, and rely only on explicit flags. For example, atllbuild would not inject any optimization flag in this case, and so whatever the user specifies in compile-options would control.  

Finally, we would introduce well-known overlays like we already have for atbuild.platform. However, this would be a hook for the user to customize their settings on a per-configuration basis (like it works for platforms), not the primary implementation of the feature.

Alternatives considered

Unlike upstream, I will generate real alternatives.

Ad-hoc configurations

There are more kinds of configurations than debug/release. For example, there are things like test (as distinct from interactive debugging), benchmark (which needs production optimizations, and testing, but not necessarily other production options), and so on. An argument can be made that everybody should roll their own configurations (after all this is Anarchy Tools).

However I believe we can handle these cases either by adding new configurations or by using the existing overlays system.

One drawback of the proposal is that if we extend configurations later, individual tools may see configurations they don't understand and may not be sure how to behave. It may be worth defining get them up-front, even if there is some speculation about whether they will be used.

tags

Another alternative would be to have configurations that are global, but not mutually exclusive. Behind this door we would have configurations like optimized, unoptimized testing, etc. And so release would really be an alias for optimized, whereas benchmark would be an alias for optimized+testing. So a tool that only cares about optimization could listen for a consistent flag, rather than try to divine what benchmark means by itself.

This is actually my favorite alternative of all of them, but I think it may be over engineered. I suspect there are probably only five-or-so configurations anywhere. If we can generate many more, this is worth looking into.

Use overlays

Another alternative is just to use the existing overlay system as-is. It has essentially gotten us this far. Certainly I do not want to remove that system.

But the problem here is analogous to platforms. We once used overlays to implement platforms, but in the fullness of time we replaced it with a better system. In particular, the default "no platform" was problematic, and we face a similar situation with configurations.

Moreover the overlay system is complex. I believe it is important, and solves certain thorny problem no other solution can solve, but we should use it sparingly, and if there is a better approach, we should move.

In addition, tool implementations may want to condition on things that aren't actually options. For example, a strip tool may need to work slightly differently depending on whether the thing being stripped is going to be used for debugging or for releasing, but involving the user in the details of how exactly we will strip may not be meaningful. An overlay-based solution forces us to expose things as a user-configurable option, which may not make sense. If it does, we should of course do it, but it doesn't always.

One drawback of this proposal is that if an option is configurable, finding out its value becomes more complex. Whereas before it could come from the task definition itself, an overlay, or conditional platform tool implementation, it can now come from conditional configuration tool implementation as well. WMO is a case of this, where it is a user-visible option, people can (and do!) control it from overlays, and now there is a conflict.

IMO, tools should be expected to follow the user-specified (a.k.a. Overlay-specified) behavior if there is some. Only if there is no explicit value, do we follow configuration or platform-default behavior.

The interaction of these features may still be confusing, but I don't think it will be that bad. Most people controlling WMO from an overlay right now can migrate to controlling it via configuration, and those that can't, we should be backwards-compatible with.

Drawbacks

One drawback of this proposal is that it would be hard to implement chaining of configurations in a sensible way (e.g. Compiling your project for debug, but its dependencies for release). (E.g. #22 ). However there seems to be some resistance to that feature, and I'm not sure I need it if we get binary support for atpm.

owensd commented 8 years ago

I actually don't like this approach and it re-creates some of what I really dislike about SwiftPM. Configurations are more than debug and release and baking in that kind of knowledge into the toolset is something I don't like.

I'm ok with simplifying the overlay system with this subset called configuration, that's fine. I'm even ok with having default configurations like you have: debug, release, and none.

However, I think this is one of the things Xcode did correctly with the xcconfig system: it provides you the default config names and provides defaults values for those configs based on the type of project you want to create. BUT it provides you with the full freedom to change, rename, or completely remove these configs as needed.

Coupling functionality in atllbuild with a debug config name is the wrong way to go about it. Instead, the debug config should set some type of variable, like ATLLBUILD_VARIABLE_FOR_SPECIAL_INTERACTIVE_DEBUGGING_GOODNESS that it keys off of. This provides users the ability to create their own configs and still leverage the functionality of atllbuild.

drewcrawford commented 8 years ago

I actually don't like this approach and it re-creates some of what I really dislike about SwiftPM. Configurations are more than debug and release and baking in that kind of knowledge into the toolset is something I don't like.

I don't know how to solve objections of the form "I don't like it". Can you produce concrete objections?

Configurations are more than debug and release

While I don't disagree with you, can you produce a list of ten or so configurations you use? I max out at 5. If you can generate 50, I will come around to your point of view, but if you can only generate five, I'm not sure there are substantially more than debug/release in the sense that fixed configurations are obviously unscaleable to the problem. Two might be unscaleable, but you're arguing something different about the kind of solution, not the size of N.

Also, if your objections are limited to "I need more configurations", what is your beef with the overlays system? It is flexible enough to handle your case. I don't suggest we deprecate it. I think it is a fine system for things that need to be hand-configurable.

However, I think this is one of the things Xcode did correctly with the xcconfig system: it provides you the default config names and provides defaults values for those configs based on the type of project you want to create. BUT it provides you with the full freedom to change, rename, or completely remove these configs as needed.

Xcode has several key features that mean copying it wholesale won't work here:

  1. It generates all the boilerplate for you
  2. It provides pretty GUI tools to wrangle the boilerplate

We don't have those, and wrangling Xcode boilerplate by hand isn't manageable; as the maintainer of our xcode emitter I can state that pretty confidently, no human was ever meant to read xcode formats.

What might work is something more flexible but well short of xcode complexity, but it remains to be seen what that would be. Overlays is one solution that meets both criteria, but I think we are here because overlays are not ideal for the configuration problem.

One part of the boilerplate issue is that currently, the defaults are not right. If you just create a random atllbuild task like you see in our documentation:

:task {
    :tool "atllbuild"
    :name "foo"
    :type "executable"
    :sources ["src/**.swift"]
}

This task is wrong, no matter what you're trying to do. If you wanted an optimized/production build, it's wrong. If you wanted a debug build it's wrong. If you wanted to do testing, it's wrong. IMO, our tools should do something useful by default, and currently they do not. (Or they should force you to pick a default, like we do with the nop tool). Maybe good defaults seems SwiftPM-like, maybe that seems magical, but the difference is I want a nop configuration that lets you revert back to doing nothing useful so the programmer can write boilerplate when they actually need that control. So it is more a question of producing useful defaults than removing configurability.

Second I think there should be some obvious "10-character" way to solve all the "boring" problems. If somebody wanted just a sanely optimized build, they should not need to delve into the documentation to remember ATLLBUILD_VARIABLE_FOR_SPECIAL_INTERACTIVE_DEBUGGING_GOODNESS. Yes, if they need Ounchecked or something unusual, consult the documentation. But if you just want to produce an optimized build, you just want to test, (like everybody does in every program), there should be a way to solve your problem in 10 keystrokes or less, without delving into arcane compiler details, and without ever opening our documentation to remember how to do it. If you have unusual problems, by all means, we support that from overlays (or some other system, or we make this one more flexible) but we need to have a simple way to solve the simple problems for when someone just wants an optimized build without whatever deep programming problem they are holding in their head to fall out in the process.

141z4m

Xcode supports the second usecase by generating the debug/release/test boilerplate for you. But we don't, so we need a different solution to that problem.

Instead, the debug config should set some type of variable, like ATLLBUILD_VARIABLE_FOR_SPECIAL_INTERACTIVE_DEBUGGING_GOODNESS that it keys off of.

In addition to the boilerplate issue, which I feel is substantial, several "tool coordination problems" were presented in the motivation:

These tools may not necessarily live in atbuild itself, and so it doesn't make sense for atbuild to set variables on tasks when it doesn't own the tool definitions and doesn't know what they do.

You don't specify where the variables should live (a task key?) but we probably also have "task coordination problems". e.g. building atbuild involves some 5 tasks across 3 packages. If you want an optimized build and the remedy available to you is to start editing atfoundation's build.atpkg by hand we have failed.

owensd commented 8 years ago

While I don't disagree with you, can you produce a list of ten or so configurations you use? I max out at 5. If you can generate 50, I will come around to your point of view, but if you can only generate five, I'm not sure there are substantially more than debug/release in the sense that fixed configurations are obviously unscaleable to the problem. Two might be unscaleable, but you're arguing something different about the kind of solution, not the size of N.

If you can come up with 5, you've already shown that creating debug and release as magic items is insufficient.

Also, if your objections are limited to "I need more configurations", what is your beef with the overlays system? It is flexible enough to handle your case. I don't suggest we deprecate it. I think it is a fine system for things that need to be hand-configurable.

Overlays and configurations are going to need to interact with one another. So what does it mean to run atbuild --configuration release --overlay superfast where the --overlay superfast changes the optimization settings? You cannot just assume certain things in the atbuild tools based on the configuration being release.

We don't have those, and wrangling Xcode boilerplate by hand isn't manageable; as the maintainer of our xcode emitter I can state that pretty confidently, no human was ever meant to read xcode formats.

There's a significant difference between modifying the Xcode projects and the xcconfig files. I (and others I work with) wrangle those by hand all the time. It's not fundamentally different than working with atpkg files.

I would think that configurations are simply a way to specify what the overlays are to be used, in addition to providing some logic that states you must specify which configuration to be used (or we can default it one as specified in the atpkg file).

In addition to the boilerplate issue, which I feel is substantial, several "tool coordination problems" were presented in the motivation:

  • strip behavior should be different in debug/release

I don't agree that the name of a configuration should specify the behavior of tools.

Should strip really behave the same in every release build? What if I'm trying to track down an issue with missing symbols that only happens to occur in release builds? How do I specify the right configuration/overlay mechanics to trick the strip tool to not run? Maybe in this case I can modify the atpkg file to get strip to stop running for some particular versions of optimized builds?

Or maybe I need packageatbin to always run with good compression because something in my workflow exposes a bug when fast compression is enabled.

I don't see how this is going to be possible with your proposal. Maybe I'm just missing it.

drewcrawford commented 8 years ago

If you can come up with 5, you've already shown that creating debug and release as magic items is insufficient.

You're claiming something fundamentally different: you're claiming that there's no fixed set of configurations we can generate.

I'm happy to concede that debug/release specifically are not the set. But your position requires an inductive argument: that given N configurations, we will need N+1. In that case, no fixed set of configurations can scale.

I'm asking you to present that inductive argument, because the argument you are presenting–that we need more than 2–is not really an argument against the design, so much as an argument against the first-pass implementation. I could present the 5 I use, you could present the 5 you use, and we could implement them up front. But that's not your position, unless I misunderstand.

where the --overlay superfast changes the optimization settings?

Going back to the proposal for a minute,

IMO, tools should be expected to follow the user-specified (a.k.a. Overlay-specified) behavior if there is some. Only if there is no explicit value, do we follow configuration or platform-default behavior.

Of course, it's possible a configuration changes something for which there is no user-configurable option, but in that case, no overlay could ever conflict with it, so that conflict cannot arise.

There's a significant difference between modifying the Xcode projects and the xcconfig files. I (and others I work with) wrangle those by hand all the time.

Maybe you do, but the others you work with rely on this GUI:

screen shot 2016-05-12 at 7 50 32 pm

We don't have this GUI in our toolchain, until we do, even an XCConfig-like solution (which I concede is simpler than xcodeproj) is not manageable.

I don't agree that the name of a configuration should specify the behavior of tools

Well what should it specify? Because atbuild is just a collection of tools, there is nothing else for a configuration to be, other than to be a thing that works on tools.

I mean, it could be an overlay. That option is available to us. But we have overlays. Are you happy with them? Or what was wrong with them that prompted you to open the issue?

Should strip really behave the same in every release build? What if I'm trying to track down an issue with missing symbols that only happens to occur in release builds? How do I specify the right configuration/overlay mechanics to trick the strip tool to not run? Maybe in this case I can modify the atpkg file to get strip to stop running for some particular versions of optimized builds?

You could

Your objection is more properly presented for the inverse case: getting "release" strip behavior without also getting it for the build tool. I don't know if that's a real situation or not, but it's a weakness in my design.

Or maybe I need packageatbin to always run with good compression because something in my workflow exposes a bug when fast compression is enabled.

Maybe we should just avoid having things that aren't configurable directly (in addition to being configurable by configuration). I think there are some cases where that is absurd, but it would solve this class of objection, so maybe being absurdly configurable is not terrible.

drewcrawford commented 8 years ago

Fun fact, I'm sitting in a meetup and this just happened.

A: Do you know what xcode schemes are? B: Unfortunately.

drewcrawford commented 8 years ago

Stepping back a minute, I think we have two problems here:

  1. Wrangling a large number of small projects. e.g., Anarchy Tools itself. Our tool-to-developer ratio is like 10:1, so we need some way to get simple, consistent, optimized builds (debug builds, test builds) across all of them without any hassle. Adding even one line to your atpkg is a huge deal because it is 10 lines across 10 projects, 10 commits, 10 CI runs, etc.
  2. Wrangling a small number of large projects. e.g. if you are compiling Excel. Your tool-to-developer ratio is like 1:10 (or worse), so you need a powerful system that can handle lots of weird workflow cases all in the same project. Adding even 30 lines to your atpkg is no big deal because it's one commit. And it's worth investing in doing complex boilerplate because those 30 lines are enjoyed by 10 other developers working on the project, so workflow win.

Both of these are legitimate cases. We should support both.

My suggestion is, XCConfig-based designs favor the second category. Xcode favors the second category, and Apple in general optimizes for that category. Overlays favor that category too, but maybe there is something wrong with them I don't understand because I'm not living in large project land right now. I have nothing against this, they're a valid case.

But my immediate concern is case 1–how do we get optimization enabled across tens of small repos that I'm in charge of maintaining? How do we get testability across all the CLI tools I maintain?

XCConfig is not a solution to that problem. Creating custom configurations for 40 projects is not a solution, disabling WMO in 40 projects when it breaks Swift-wide (#92) is not a solution. Defining defaults that work for 90% of my projects and then filling in the last 10% of the weird cases with overlays or some other explicit configuration sounds like a serious solution.

That's really what I want to accomplish here–a feature that turns on optimization for all AT projects without a lot of hassle. There are other ways to go about it that don't involve the current proposal, but XCConfig isn't a solution to my problem.

owensd commented 8 years ago

You're claiming something fundamentally different: you're claiming that there's no fixed set of configurations we can generate.

Actually, my claim is even more broad than that: debug and ship mean different things to different projects, and that team should decide on their own what to call their configs (for example, some use chk and fre instead of debug and ship). It's hard for me to see how a configuration should be anymore than a named collection of properties that flows through the system.

Real build configurations that are used: debug, codecov, ship, and final.

Basic differences:

There's a significant difference between modifying the Xcode projects and the xcconfig files. I (and others I work with) wrangle those by hand all the time.

Maybe you do, but the others you work with rely on this GUI:

No, people don't rely on that UI because the only items you can modify in that UI are the project-level items. Any items from external xcconfig files cannot be edited. It is a useful debug UI, but it's not a general use editor.

Fun fact, I'm sitting in a meetup and this just happened.

A: Do you know what xcode schemes are? B: Unfortunately.

Schemes are not the same thing as configs, so I'm not really sure the relevance of the comment... every scheme points to a specific build configuration though.

Wrangling a large number of small projects. e.g., Anarchy Tools itself. Our tool-to-developer ratio is like 10:1, so we need some way to get simple, consistent, optimized builds (debug builds, test builds) across all of them without any hassle. Adding even one line to your atpkg is a huge deal because it is 10 lines across 10 projects, 10 commits, 10 CI runs, etc.

Yeah... and I solve this by importing a single, shared set of common options. This file gets imported by the ten different tools. So one single update solves this. This is also how I solve this problem with xcconfig files.

Wrangling a small number of large projects. e.g. if you are compiling Excel. Your tool-to-developer ratio is like 1:10 (or worse), so you need a powerful system that can handle lots of weird workflow cases all in the same project. Adding even 30 lines to your atpkg is no big deal because it's one commit. And it's worth investing in doing complex boilerplate because those 30 lines are enjoyed by 10 other developers working on the project, so workflow win.

Maybe for a monolithic, single target app that could be true. However, with large projects that are broken up into multiple shared components, then bullet 1 is still the more accurate one.

My suggestion is, XCConfig-based designs favor the second category. Xcode favors the second category, and Apple in general optimizes for that category. Overlays favor that category too, but maybe there is something wrong with them I don't understand because I'm not living in large project land right now. I have nothing against this, they're a valid case.

I disagree with this claim for xcconfig files. For Xcode, yes, but Xcode just wants to work with projects files and we actively put as little information as possible in those and rely solely on xcconfig files to provide all of the build setting information. We use xcconfig files at multiple layers to provide and build up a set of build options that are shared across a large number of projects. Applications then provide an xcconfig file to provide the last bit of customization required, such as the app name.

But my immediate concern is case 1–how do we get optimization enabled across tens of small repos that I'm in charge of maintaining? How do we get testability across all the CLI tools I maintain?

To re-iterate, I think this is solved by:

That's really what I want to accomplish here–a feature that turns on optimization for all AT projects without a lot of hassle. There are other ways to go about it that don't involve the current proposal, but XCConfig isn't a solution to my problem.

We (Anarchy Tools) have that through importing overlays, and we (day job) use xcconfig files to solve that exact problem.

drewcrawford commented 8 years ago

Actually, my claim is even more broad than that: debug and ship mean different things to different projects, and that team should decide on their own what to call their configs (for example, some use chk and fre instead of debug and ship). It's hard for me to see how a configuration should be anymore than a named collection of properties that flows through the system.

The core problem here is one of dependencies. When you have 10 direct dependencies and 30 indirect dependencies and you build your thing with configuration chk, what configuration do you pick to build the rest of the graph?

Xcode gets this wrong–it assumes there will be a chk configuration in all the dependencies. But there's not one, because chk is something your team made up. And since there isn't, it either doesn't even build or picks a configuration at random, and your only remedy is to go edit files you don't own.

We need some kind of solution to this "pick configurations for your dependencies" problem. I proposed a design for this on #22, but there were (potentially valid) objections. But if we are going to go back to overlays as a configuration design we will need to go back to #22 as a solution to the problems inherent to dependency overlay configuration.

debug - unoptimized, unsigned, fully testable builds. codecov - unoptimized, unsigned, fully testable builds, instrumented builds. ship - optimized, signed, fully testable. final - optimized, signed, testable surface removed.

Is there a reason you are not using different tasks for these?

codecov in particular sounds like a task–I imagine you would have some kind of tool after the build to generate HTML reports or whatever. Sure, you would want a mechanism like #22 to control the actual build configuration dependent task, but this problem feels very task-like.

More broadly, I wonder if with task names it would fit into my design this way:

task name "configuration" your atpkg overrides
debug debug +test
codecov debug +test, +instrument
ship release +test
final release

Or, if we went ahead and built out "test" (unoptimized, testable) and "bench" (optimized, testable) configurations as well, we could get your problem down to a single special case:

task name "configuration" your atpkg overrides
debug test
codecov test +instrument
ship bench
final release

Now the debug, ship, and final tasks are actually identical, so we could combine them and you would only need to maintain default and codecov yourself. Not that you would have to do that, obviously, but it seems convenient. And it pairs well with my intuition that codecov is doing post-build work and so should be a task, whereas the others are less obviously tasklike (and in fact under my design they don't need to be).

And now not only do we get the maintenance benefit of cutting 90% of your boilerplate, but we solve the dependency problem too. e.g. we are confident that all your dependencies will have a debug, release, etc. configuration, because it's built-in. If you want to find out the codecov for your dependencies, you are out of luck, but that seems like the kind of problem where it's reasonable to expect someone to cobble tools together by themselves.

If we compare this to what you would do with overlays / xcconfig:

task name --use-overlay your overlay definition
default debug +unoptimized +test
default codecov +unoptimized +test +instrument
default ship +optimized +signed +test
default final +optimized +signed

I literally can't think of any reason you would prefer this over my table:

This is the reason that I'm convinced that the design is better than xcconfig.

every scheme points to a specific build configuration though.

No, every scheme points to exactly five build configurations. There's a unique choice for run, test, profile, analyze, and archive.

Yeah... and I solve this by importing a single, shared set of common options. This file gets imported by the ten different tools. So one single update solves this. This is also how I solve this problem with xcconfig files.

You've solved it in the sense that you've declared it solved, but I don't understand the mechanism. Where does the shared file live?

  1. If it's shared "on my harddisk" then that's not shared in a way that CI will produce consistent builds
  2. If it's shared "in the repo" then it's still 40 commits for 40 projects to turn off WMO when it breaks (see #92 )
  3. Maybe you mean to suggest that we should have a anarchy-config repo that holds the definitions for the common AT configurations, and everybody takes a dependency on that?

We could do that (and it feels very AT-like to spawn yet another project) but I'm not yet convinced the problem warrants decoupling it from atbuild. The usual rule for core inclusion is "do most people need this"? I would argue that optimized and unoptimized configurations are things most people need.

If we create a repo with debug/release overlay configs or whatever, all that will happen is that 95% of projects will require an additional build dependency. With package-framework or package-deb or our other satellite build dependencies, only a third of projects need them, and they all need a different third.

We (Anarchy Tools) have that through importing overlays, and we (day job) use xcconfig files to solve that exact problem.

I'm now asking again because I'm not sure if this is an answer to my question. Are you satisfied with overlays for this problem?

Because you opened this issue, and we had overlays at that time, so I assume at one point you felt they were insufficient. But now your argument is dominated by "use overlays, it's like xcconfig and better".

Did your opinion of overlays change at some point? Was there some feature we added that made overlays work better for you, and so you no longer need a solution beyond what we've already shipped? Or do you still have some problem that motivates the creation of a separate "configurations" feature (apparently unaddressed by my proposal) and can you present that problem?

owensd commented 8 years ago

For clarity, I have no non-trivial projects that are using Anarchy Tools. All projects that have any complexity are built with xcodeproj and xcconfig files.

Further, I don't have any of this duplication or boiler plate that you keep referring to with the xcconfig setup, so I'm really confused by most of your examples.

The Configurations section within Xcode might look something like this:

debug - project_debug.xcconfig
  - target1 - target1.xcconfig
  - target2 - target2.xcconfig

ship - project_ship.xcconifg
  - target1 - target1.xcconfig
  - target2 - target2.xcconfig

There are other files like common_debug.xcconfig and common_ship.xcconfig that get imported by project_debug.xcconfig and project_ship.xcconfig respectively. These common files are the ones that hold all of the properties across the projects. This is where I can enabled/disable WMO with a change to a single file.

The targetN.xcconfig files contain only the target specific data, such as bundle name, plist file, etc... Notice that these files are shared across the debug and ship configurations.

If I create a new project, I make a new project_debug.xcconfig, project_ship.xcconfig, and targetN.xcconfig files. Maybe this is the boiler plate you are referring to?

The core problem here is one of dependencies. When you have 10 direct dependencies and 30 indirect dependencies and you build your thing with configuration chk, what configuration do you pick to build the rest of the graph?

Here's the thing... I don't build like that. I only build dependencies that I control with my configurations. For external dependencies, if I need to build them, I trigger a build for them as a dependency without my configuration environment. Further, it's not necessarily true that I want the same build configuration for my dependencies.

Example: I have an AWS dependency that ships as source code. It's a dependency, but I always want that code built as a "release" build regardless of what type of build I'm producing for my project.

I handle dependencies that I own, but that are not part of the project, in the same way. If the dependency is part of my project (via submodules or not), I have a shared set of config files that I use as mentioned above.

We need some kind of solution to this "pick configurations for your dependencies" problem.

Do we? Or do we simply need a mechanism to tell the dependency how to build with the ability to pass in an environment or to use a specific configuration? It's the magic passing of environment/overlays from dependency to dependency that seems to be the cause of the majority of the complication with overlays.

Is there a reason you are not using different tasks for these?

Yeah, the task is build. I can produce a build in a number of ways (e.g. configuration or overlay). The task isn't debug.

we are confident that all your dependencies will have a debug, release,

This assumption is completely based on the idea that since we provide a meaning for debug and release that everyone will just use that.

every scheme points to a specific build configuration though.

No, every scheme points to exactly five build configurations. There's a unique choice for run, test, profile, analyze, and archive.

Do you actually have these point to unique configurations? Each of those values always points back to one of my debug, ship, final, or codecov choices. If anything, schemes are equivalent to atpkg files with a limited set of tasks: run, test, profile, analyze, and archive.

You've solved it in the sense that you've declared it solved, but I don't understand the mechanism. Where does the shared file live?

  1. If it's shared "on my harddisk" then that's not shared in a way that CI will produce consistent builds
  2. If it's shared "in the repo" then it's still 40 commits for 40 projects to turn off WMO when it breaks (see #92 )
  3. Maybe you mean to suggest that we should have a anarchy-config repo that holds the definitions for the common AT configurations, and everybody takes a dependency on that?

92 seems like a perfect example of what I'm talking about. Don't have the atbuild tool key off of a debug or ship configuration, but rather, the presence of the -whole-module-optimization flag coming in from whatever configuration or overlay magic the user has decided to build the atpkg files with.

Because you opened this issue, and we had overlays at that time, so I assume at one point you felt they were insufficient. But now your argument is dominated by "use overlays, it's like xcconfig and better".

I opened this bug because I want to type this in the command line: atbuild build -p ios -c debug and get an error if I don't have a default configuration specified or I didn't pass in a -c option.

The way overlays are today, they are simply naked, semantically unbound xcconfig files missing the structure that the configuration system of xcodebuild provides (not schemes, but configurations).

So I'd simply build configurations on top of the overlay system, you could even key into them with configuration.debug or whatever. However, it would provide a little more help than what we can do with required-overlays that we have today.

drewcrawford commented 8 years ago

Further, I don't have any of this duplication or boiler plate that you keep referring to with the xcconfig setup, so I'm really confused by most of your examples.

Sure you do. Take a random xcodeproj, open it up, and stare at it.

screen shot 2016-05-16 at 11 35 54 pm

See this? It's boilerplate. 500 lines of boilerplate.

screen shot 2016-05-16 at 11 39 03 pm

If you are using configurations, you have boilerplate coming out of your ears. You may not notice it, because you have good tooling.

But we don't have that tooling. Realistically, if you want to replicate Xcode's "Release" behavior right now you can't because we don't even expose the options (atllbuild only has 20 options, Xcode has almost 400). And even if we did, it would require hundreds of lines of config to match what Xcode does in a Release config by default.

If I create a new project, I make a new project_debug.xcconfig, project_ship.xcconfig, and targetN.xcconfig files. Maybe this is the boiler plate you are referring to?

Yes and no. Let me illustrate this with a picture.

screen shot 2016-05-16 at 11 53 45 pm

There are four columns here, all of which can specify options. We have equivalents for some of these columns, and not for others. We have no equivalents for 400 rows of options, as we expose 40 or so at most.

And all of these things, which are all things I have to do besides write my program, I call "boilerplate". I exclude from this definition things that are specific to my program (e.g. my program has its own bundleID, my program needs its own provisioning profile, it has its own codesigning, etc). But I'm talking about all the things that are common to most programs like should we generate debug info, should we strip a dsym, should we optimize, should we enable the DEBUG preprocessor macro, should testability be enabled, should we turn on WMO, should bitcode be enabled, and so on.

These are all choices that Xcode makes for you. Sure, you need to override sometimes, but 99% of the time you don't, and there should be a sane default behavior so that you can get to "hello world" quickly without you having to write hundreds of lines of config.

Again, Xcode solves this problem by writing the hundreds of lines of config for you. But I don't believe that approach will work for us; we don't have the tooling to support it.

Here's the thing... I don't build like that. I only build dependencies that I control with my configurations. For external dependencies, if I need to build them, I trigger a build for them as a dependency without my configuration environment.

First of all, atbuild doesn't support using a precompiled binary dependency, see #13, see https://github.com/AnarchyTools/atpm/issues/1. We should support it, obviously, (unlike upstream which rejected this feature lol) but right now we only support building everything from source.

But in order for me to be able to justify working on that, I first need to land configuration support, because the projects I need to migrate to AT actually prefer building from source for their current dependencies. So the fastest path for me to land that feature is to get a resolution here.

Further, it's not necessarily true that I want the same build configuration for my dependencies.

I did not say it was. What I said was this is Xcode's current behavior. It tries to find a configuration with the same name and if it can't find one, it picks one at random and/or blows up with no particularly good solution other than editing files you don't own.

This is an inherent problem with any underspecified configuration system. If somebody can just go in and delete their Release configuration (or never write one to begin with like in... all our current projects) then we can never even hope to pick a sane default for dependency building under any circumstances. If we have a minimum built-in configuration set debug/release(/test/bench/profile) then we can talk about what a sane default would be, whether that's always building dependencies for release (as you do) alternating between debug/release (as I might advocate), matching the profile (as some might advocate), or even letting library authors recommend their own solution inside the atpkg.

But the whole conversation is a nonstarter if configurations are the Wild West. If somebody doesn't even have a release configuration, then what do you do? You have no rule of thumb for that case. It doesn't arise often in Xcode (but it definitely can) because it requires actual effort to delete a Release config. But in our situation, it requires actual effort to create one. Therefore we do not have any release configs for any of our current projects. How are you going to take a dependency on any of them?

Or do we simply need a mechanism to tell the dependency how to build with the ability to pass in an environment or to use a specific configuration? It's the magic passing of environment/overlays from dependency to dependency that seems to be the cause of the majority of the complication with overlays.

No, the problem with overlays is that nobody has bothered to write debug, release, or "whatever" overlays for projects like atfoundation, atpkg, etc. There is no current convention for that, and it was assumed that this issue would either solve the problem at the atbuild level without requiring atpkg changes for typical cases, or require a few lines of atpkg changes at most to get the essential debug/release behavior.

What is not expected is a decision of the form "expose 350 more options such as VALIDATE_PRODUCT, COPY_PHASE_STRIP GCC_PREPROCESSOR_DEFINITIONS, MTL_ENABLE_DEBUG_INFO etc., create xcconfig-like files, check them into each project, and manage them yourself, good luck".

Do you actually have these point to unique configurations?

Typically, yes. I mean perhaps not all 5, but 4 or so is typical for a project I work on full-time. For a project I don't work on full-time, it generally scales linearly with how much time I spend on the project.

If anything, schemes are equivalent to atpkg files with a limited set of tasks: run, test, profile, analyze, and archive.

Not at all. I mean sure, it is necessary, but it is not sufficient. You probably don't want to debug a release build, for example. So if your debug task is actually an lldb shell task (which would make some amount of sense), then you need some way to configure the actual build task itself, such as to compile with -g, to emit the right DWARFs so lldb will find our symbols, to disable optimizations, us bitcode markers instead of bitcode sections (but only on certain platforms and only when used with closed-source Apple clang, how do we detect it?) and a few dozen other options.

Creating the tasks that launch the debugger (the test runner, the profiler) is not really the interesting part of that problem. The interesting part is how we build the desired target "for debugging", "for profiling", etc. without making the programmer specify hundreds of lines of very complex, brittle boilerplate, changing from one snapshot to another, just to get a build that shows backtraces in lldb.

Don't have the atbuild tool key off of a debug or ship configuration, but rather, the presence of the -whole-module-optimization flag coming in from whatever configuration or overlay magic the user has decided to build the atpkg files with.

There is actually only one project I'm aware of (and it's not one of ours) that seriously tries to use WMO at all.

The problem is not deciding how programs should specify they want WMO. We have a syntax for it, maybe it is stupid, open an issue and complain about whatever stupid syntax I came up with that day.

The problem is that given the fact that a syntax exists, nobody is bothering to use it. The problem is, atfoundation, atpkg, atbuild, atpm, package-framework, xcode-emit all have some syntax they could use to enable WMO, and they are all too lazy to have bothered to add the lines to their atpkg. Right behind them I have another 5-6 private projects which also could enable WMO and only one of them has bothered, and even then it uses at least 6 dependencies that have not bothered.

Now perhaps in your view these projects are just lazy, and they need to check in their 5 atpkg lines each to create a WMO overlay and be done with it. Fine. But is that also the resolution for toggling -g, for emitting DWARFs, dealing with the undocumented bitcode options, emitting .symbols files, sorting out the .bcsymbolsmap, and all the other things that go into the default configurations that Xcode doesn't require you to sort out by yourself? Because this is not going to work.

What these projects are actually doing isn't being lazy, it's holding out for a resolution that doesn't require the micromanagement of detecting Apple-clang vs actual-clang in order to build hello world for release, which is real boilerplate you have in your projects right now.

owensd commented 8 years ago

Sure you do. Take a random xcodeproj, open it up, and stare at it.

I don't think you understand what I'm saying: all of our projects have no build settings specified in the Xcode project file.

Steps to Create a New Project

  1. Create a new Xcode project
  2. Open up the build settings
  3. Select all build settings
  4. Delete
  5. Pick the xcconfig files in the configuration section

Literally, the buildSettings value in the .pbxproj file is: buildSettings = {}; We move all the "boiler plate" to managed xcconfig files.

Just to be explicit here, I'll answer your claims:

  • Every time you create an xcodeproject, you get boilerplate.

Nope, we delete it.

  • Every time you create a new configuration, you get boilerplate.

Nope, it's been deleted so it's simply duplicating nothing.

  • You cannot even create a new configuration from scratch (at least in an Xcode-supported way, maybe you know how to do this by hand). The only way to create a new configuration is to clone the boilerplate from an existing one:

Yep, but since the configurations have been reduced to a set of which xcconfig file to use, this is effectively the same.

If you are using configurations, you have boilerplate coming out of your ears. You may not notice it, because you have good tooling.

Nope, we've removed it. We use text editors to write the xcconfig files and Xcode to do the rest, so we don't even have good tools either.

We have no equivalents for 400 rows of options, as we expose 40 or so at most.

I count 137 options broken down over a few different categories. Some of those pertain to tools we haven't even written yet, like deployment or archiving.

These are all choices that Xcode makes for you. Sure, you need to override sometimes, but 99% of the time you don't, and there should be a sane default behavior so that you can get to "hello world" quickly without you having to write hundreds of lines of config.

There's huge difference between having these defaults built-in vs. being able to use them easily when wanted. Your proposal is akin to having those options being set and I need to override them to get different behavior. So any time a new version of Xcode comes out, I need to now worry about what new settings have been added so I can do modify them as needed. This strategy is extremely error prone and fragile. It also leads to unexpected behavior on upgrades.

I don't really know how to address the rest of your post as most of it is hinging on a complete misunderstanding of how I'm using the xcconfig files and your assumption that Xcode is having a much bigger role in the picture than it really is. It's simply the ferry boat for all of our configuration data to pass to the compiler.

drewcrawford commented 8 years ago

Let me condense this conversation.

It seems very likely to me at this point that you have a much higher tolerance for handrolling flags than I do. I still believe you underestimate the amount of work involved, you still believe I underestimate the extent to which you actually do it, prosecuting the fact pattern further is tedious and may not actually lead anywhere useful.

So let's stipulate for the sake of argument that you are indeed significantly more willing to handroll flags that I tend to find irksome. Can we task you with handrolling all configuration flags for the AT projects, including such AT projects as are apparently news to you? (e.g. our packaging and deployment projects).

If so, that still does not resolve the problem for my private projects, but if a problem is uniquely private, I am content to ship solutions privately. Not all my build annoyances need to be resolved upstream.

Or if not, I would find arguments of the form "managing flags is easy" to be unconvincing, because easy or not, we have no volunteers to do it.

owensd commented 8 years ago

Sure. I still think the idea of enabling configurations is worthy, we seem to have different ideas of what that might be.

I'm also not opposed of having all of these defaults there, as long as it's easy to not use them and provide your own set.

Anyhow, I'll look into it.

drewcrawford commented 8 years ago

I don't really know how to make progress on the actual problem here, as we seem to disagree on the core issues.

For my part, I'm just flat-out not willing to handle the flag wrangling for my projects–I've gone this far and I will go no further. For whatever it's worth, my projects are way less manageable than AT, but a solution for me would have a trickle-down effect across our projects as well.

For your part, you seem quite concerned about exactly those properties of the solution that would relieve me of my burden–e.g. you're worried about changing defaults, but that's my design goal, because changing the defaults relieve me of managing each project.

The only path forward I can see is either to convince you that in reality you want what I want, or to go off and build something separately.

I have made a solid attempt at the former, but we're approaching the point where the latter will be more efficient. It may very well be that we have a different philosophy on this point, and I don't see a good way to reconcile them.

owensd commented 8 years ago

Make the changes you want for the configuration as proposed. I've given my objections, but honestly, I have less skin in the game.

This is still my key take away:

I'm also not opposed of having all of these defaults there, as long as it's easy to not use them and provide your own set.