options.go 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. /*
  2. Copyright 2023 Docker Compose CLI authors
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package compose
  14. import (
  15. "fmt"
  16. "github.com/compose-spec/compose-go/types"
  17. "github.com/docker/compose/v2/pkg/utils"
  18. )
  19. func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
  20. defaultPlatform := project.Environment["DOCKER_DEFAULT_PLATFORM"]
  21. for i := range project.Services {
  22. // mutable reference so platform fields can be updated
  23. service := &project.Services[i]
  24. if service.Build == nil {
  25. continue
  26. }
  27. // default platform only applies if the service doesn't specify
  28. if defaultPlatform != "" && service.Platform == "" {
  29. if len(service.Build.Platforms) > 0 && !utils.StringContains(service.Build.Platforms, defaultPlatform) {
  30. return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", service.Name, defaultPlatform)
  31. }
  32. service.Platform = defaultPlatform
  33. }
  34. if service.Platform != "" {
  35. if len(service.Build.Platforms) > 0 {
  36. if !utils.StringContains(service.Build.Platforms, service.Platform) {
  37. return fmt.Errorf("service %q build configuration does not support platform: %s", service.Name, service.Platform)
  38. }
  39. }
  40. if buildForSinglePlatform || len(service.Build.Platforms) == 0 {
  41. // if we're building for a single platform, we want to build for the platform we'll use to run the image
  42. // similarly, if no build platforms were explicitly specified, it makes sense to build for the platform
  43. // the image is designed for rather than allowing the builder to infer the platform
  44. service.Build.Platforms = []string{service.Platform}
  45. }
  46. }
  47. // services can specify that they should be built for multiple platforms, which can be used
  48. // with `docker compose build` to produce a multi-arch image
  49. // other cases, such as `up` and `run`, need a single architecture to actually run
  50. // if there is only a single platform present (which might have been inferred
  51. // from service.Platform above), it will be used, even if it requires emulation.
  52. // if there's more than one platform, then the list is cleared so that the builder
  53. // can decide.
  54. // TODO(milas): there's no validation that the platform the builder will pick is actually one
  55. // of the supported platforms from the build definition
  56. // e.g. `build.platforms: [linux/arm64, linux/amd64]` on a `linux/ppc64` machine would build
  57. // for `linux/ppc64` instead of returning an error that it's not a valid platform for the service.
  58. if buildForSinglePlatform && len(service.Build.Platforms) > 1 {
  59. // empty indicates that the builder gets to decide
  60. service.Build.Platforms = nil
  61. }
  62. }
  63. return nil
  64. }