Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
repo: setup a foreign arch and sources #1348
Conversation
kalikiana
and others
added some commits
Jun 1, 2017
kalikiana
referenced this pull request
Jun 2, 2017
Closed
go plugin: cross-compilation support #1253
kalikiana
changed the title from
Foreign arch src
to
Setup a foreign arch and sources
Jun 2, 2017
kalikiana
self-assigned this
Jun 2, 2017
| + def _setup_foreign_arch(cls, arch): | ||
| + native_arch = subprocess.check_output( | ||
| + ['dpkg', '--print-architecture']).decode( | ||
| + sys.getfilesystemencoding()) |
| + sources_arch_file.flush() | ||
| + sources_lists = os.path.join('/etc/apt/sources.list.d/', | ||
| + 'ubuntu-{}.list'.format(arch)) | ||
| + subprocess.check_call(['sudo', 'cp', |
elopio
reviewed
Jun 14, 2017
I reviewed the last commit, and I like it. Do you think this is something Colin should review?
And maybe a manual test?
kalikiana
changed the title from
Setup a foreign arch and sources
to
repo: setup a foreign arch and sources
Aug 8, 2017
sergiusens
added this to the 2.35 milestone
Aug 10, 2017
sergiusens
referenced this pull request
Aug 10, 2017
Open
foreign arch and sources setup for `build-packages` #1476
| + """If the given package has an arch suffix: | ||
| + Generate arch-specific sources list | ||
| + Update the package cache to pull in the new packages | ||
| + Verify that the package is in the cache |
kyrofa
Sep 8, 2017
Member
It's not completely clear from the function name or the docstring what the return value indicates.
| @@ -34,11 +34,19 @@ def check_for_command(command): | ||
| def get_pkg_name_parts(pkg_name): |
kyrofa
Sep 8, 2017
•
Member
This is getting complex, and looks error-prone. I can imagine myself asking "what's the second arg... version or arch?" and "the arch doesn't include the colon, right?"
I also see us using name + arch a lot below. I suggest making this a super simple class with a few helper methods to do that type of thing for us, and return an instance of that class here.
kalikiana
Sep 15, 2017
Collaborator
I dropped those changes from this PR, after moving to using the error message to render instructions needed to setup the host.
| + return | ||
| + logger.info('Adding foreign architecture {}'.format(arch)) | ||
| + subprocess.check_output( | ||
| + ['sudo', 'dpkg', '--add-architecture', arch]) |
kyrofa
Sep 8, 2017
•
Member
I know I've said this before, but I'll add my voice here again: I personally don't like this. It means running the wrong snapcraft.yaml on my host will garbage up my arch list and sources. The only place this makes sense is when we're building in containers by default, but we're not there yet. Is there any way we can just warn if we're not building in a container?
kyrofa
Sep 12, 2017
•
Member
Yes, that's exactly what I want. Warn telling me what I need to do if I actually want to build this snap (e.g. this command). Ahem I suppose that would be an error, not a warning.
kalikiana
Sep 14, 2017
Collaborator
Ah, yeah. I see what you're saying now. We could basically generate the necessary commands and leave it up to you to decide which way to go - to my mind, that's the crucial bit, make it as discoverable as possible what's needed to continue, since it's just tedious to find this out for yourself if you've never done it.
How about something like The host needs to be setup for the target architecture. You can use cleanbuild or set SNAPCRAFT_CONTAINER_BUILDS=1 to build in a container that's automatically set up for cross-compiling. To setup the local machine, run the following commands: [...].
Long term with #1458 we could also consider a prompt like Building in a container is recommended for cross-compiling [...] [y/n]
| with apt.Cache() as apt_cache: | ||
| try: | ||
| cls._mark_install(apt_cache, package_names) | ||
| except errors.PackageNotFoundError as e: | ||
| - raise errors.BuildPackageNotFoundError(e) | ||
| + if cls._setup_multi_arch_sources(apt_cache, e.package): | ||
| + pass # FIXME? |
kyrofa
Sep 8, 2017
Member
The PR description doesn't indicate that further work needs to be done. The FIXME isn't particularly helpful, either. What happens if a release contains this before it's fixed? Should we raise an NotImplementedError or something instead?
kalikiana
Sep 12, 2017
Collaborator
Good catch, I didn't mean to leave that there. It was supposed to be a reminder for myself as I had to re-think the code after merging, but I apparently forgot. Will fix it.
| @@ -413,7 +533,7 @@ def _format_sources_list(sources_list, *, | ||
| 'release': release, | ||
| 'suffix': suffix, | ||
| 'security': security, | ||
| - }) | ||
| + }).replace('deb', 'deb [arch={}]'.format(deb_arch) if foreign else 'deb') |
kyrofa
Sep 8, 2017
Member
I'm curious why we're replacing here as opposed to making it a templated param? We should probably be catching KeyErrors here, by the way, and turning them into warnings since this can come from custom plugins as well.
Assuming we went this route for a good reason, we need to make sure it's robust to sources like deb http://debian.mycompany.com/[...].
kalikiana
Sep 14, 2017
Collaborator
I sorta considered this internal API used only but Snapcraft itself. But actually you're right, this would benefit from some error handling. The replace was meant to avoid having [arch= in there when it's not needed, but I should use an $arch variable for that.
kyrofa
Sep 14, 2017
Member
Or just use a $deb variable for the whole thing, yeah, something like that.
kalikiana
added some commits
Sep 12, 2017
| + }) | ||
| + except KeyError as e: | ||
| + raise errors.SnapcraftError( | ||
| + 'Sources list is missing the variable ${' + str(e) + '}') |
kyrofa
Sep 15, 2017
•
Member
I fell prey to this as well. This error message is a little backward: a KeyError is raised if the template is not satisfied, not if a substitution was requested but not performed. Code will be more clear:
>>> import string
>>> t = '$food is delicious'
>>> string.Template(t).substitute(food='sushi')
'sushi is delicious'
>>> string.Template(t).substitute(food='sushi', bar='baz')
'sushi is delicious'
>>> string.Template(t).substitute(bar='baz')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/string.py", line 129, in substitute
return self.pattern.sub(convert, self.template)
File "/usr/lib/python3.5/string.py", line 119, in convert
val = mapping[named]
KeyError: 'food'
So the error should probably be something more along the lines of "Cannot complete substitution in sources list: unable to define $foo".
kyrofa
Sep 15, 2017
Member
There doesn't seem to be a way to warn in the reverse situation... hmm...
kalikiana
Sep 19, 2017
Collaborator
You made me wonder what's actually an error here: if you're using a varaible ${foo} that we don't know how to fill in, that seems wrong - but using all the variables isn't necessary.
So, I updated the error based on your wording suggestion and added two test cases for the above mentioned cases, one of them failing, the other one passing. It seems to make sense this way... but let me know what you think.
| + @property | ||
| + def message(self): | ||
| + message = super().message | ||
| + # If the package was multiarch, try to help. |
kyrofa
Sep 15, 2017
Member
This looks like it's building on what used to be in errors.PackageNotFoundError. What is the rational for essentially redefining it here as opposed to moving this logic back into it?
kalikiana
Sep 18, 2017
Collaborator
What do you mean by redefining?
My idea was to move the logic into the error because it needs to know
- if the arch is valid
- if the arch was registered
- if there's sources for the arch
_mark_installdoesn't need to make a distinction if we always want it to fail - previously I had the code in there because it would have had to continue iterating through the packages after automatically setting up the host system.
Also, we might be able to re-use the error with all its details. There's a case of except PackageNotFoundError as e: raise BuildPackageNotFoundError and another instance of raising PackageNotFoundError. Maybe this could be a follow-up.
Maybe having helper methods like has_arch still makes sense when I'm adding the container side of things, but aside from that I feel it would become less readable to do that in the same method as figuring out an error message.
kalikiana
Sep 18, 2017
Collaborator
Ha, after typing all this, and reading your question once more, I realize you're probably referring to the fact that _deb.PackageNotFoundError is a subclass.
I did that because the logic is completely specific to Debian-based distros and the is this a deb distro-check is implied by being inside _deb as well. Presumably once there's a Fedora/dnf backend it will have completely different code (or none at all) depending on how architectures are handled there.
kalikiana
added some commits
Sep 19, 2017
| - 'Sources list is missing the variable ${' + str(e) + '}') | ||
| + raise ValueError( | ||
| + 'Cannot complete substitution in sources list: ' | ||
| + 'unknown variable ${' + str(e.args[0]) + '} in template') |
kyrofa
Sep 19, 2017
Member
Looks good, one nit: the format for a variable doesn't REQUIRE braces, it simply supports them. I wonder if simply using the variable name here would be better instead of assuming a specific format.
kalikiana
Sep 21, 2017
Collaborator
I was kinda going by the fact that we use braces in our templates... but I thought of another option, I'm checking now which one was actually used to refer to it in the error message.
kalikiana
added some commits
Sep 21, 2017
|
stale |
sergiusens
closed this
Oct 9, 2017
|
Why did you close this? It's not stale: the last commit, addressing the test failure, is from Sunday. |
kalikiana
reopened this
Oct 11, 2017
|
|
kalikiana commentedJun 2, 2017
•
Edited 1 time
-
kalikiana
Jun 2, 2017
Automatically add the target architecture viadpkgand setup apt sources.list for multi-arch if needed.Fixes: bug 1660903
Follow-up on Figure out multi-arch build dependencies