def _run_scriptlet(self, scriptlet_name: str, scriptlet: str, workdir: str) -> None: with tempfile.TemporaryDirectory() as tempdir: call_fifo = _NonBlockingRWFifo( os.path.join(tempdir, 'function_call')) feedback_fifo = _NonBlockingRWFifo( os.path.join(tempdir, 'call_feedback')) env = '' if common.is_snap(): # Since the snap is classic, $SNAP/bin is not on the $PATH. # Let's set an alias to make sure it's found (but only if it # exists). snapcraftctl_path = os.path.join(os.getenv('SNAP'), 'bin', 'snapcraftctl') if os.path.exists(snapcraftctl_path): env += 'alias snapcraftctl="$SNAP/bin/snapcraftctl"\n' env += common.assemble_env() # snapcraftctl only works consistently if it's using the exact same # interpreter as that used by snapcraft itself, thus the definition # of SNAPCRAFT_INTERPRETER. script = textwrap.dedent("""\ export SNAPCRAFTCTL_CALL_FIFO={call_fifo} export SNAPCRAFTCTL_FEEDBACK_FIFO={feedback_fifo} export SNAPCRAFT_INTERPRETER={interpreter} {env} {scriptlet} """.format(interpreter=sys.executable, call_fifo=call_fifo.path, feedback_fifo=feedback_fifo.path, env=env, scriptlet=scriptlet)) process = subprocess.Popen(['/bin/sh', '-e', '-c', script], cwd=workdir) status = None try: while status is None: function_call = call_fifo.read() if function_call: # Handle the function and let caller know that function # call has been handled (must contain at least a # newline, anything beyond is considered an error by # snapcraftctl) feedback_fifo.write('{}\n'.format( self._handle_builtin_function( scriptlet_name, function_call.strip()))) status = process.poll() # Don't loop TOO busily time.sleep(0.1) finally: call_fifo.close() feedback_fifo.close() if status: raise errors.ScriptletRunError(scriptlet_name=scriptlet_name, code=status)
def _run_scriptlet(self, scriptlet_name: str, scriptlet: str, workdir: str) -> None: with tempfile.TemporaryDirectory(dir=self._partdir) as tempdir: call_fifo = _NonBlockingRWFifo(os.path.join(tempdir, "function_call")) feedback_fifo = _NonBlockingRWFifo(os.path.join(tempdir, "call_feedback")) # snapcraftctl only works consistently if it's using the exact same # interpreter as that used by snapcraft itself, thus the definition # of SNAPCRAFT_INTERPRETER. script = textwrap.dedent( """\ set -e export SNAPCRAFTCTL_CALL_FIFO={call_fifo} export SNAPCRAFTCTL_FEEDBACK_FIFO={feedback_fifo} export SNAPCRAFT_INTERPRETER={interpreter} {env} {scriptlet}""" ).format( interpreter=sys.executable, call_fifo=call_fifo.path, feedback_fifo=feedback_fifo.path, scriptlet=scriptlet, env=_get_env(), ) with tempfile.TemporaryFile(mode="w+") as script_file: print(script, file=script_file) script_file.flush() script_file.seek(0) process = subprocess.Popen(["/bin/sh"], stdin=script_file, cwd=workdir) status = None try: while status is None: function_call = call_fifo.read() if function_call: # Handle the function and let caller know that function # call has been handled (must contain at least a # newline, anything beyond is considered an error by # snapcraftctl) feedback_fifo.write( "{}\n".format( self._handle_builtin_function( scriptlet_name, function_call.strip() ) ) ) status = process.poll() # Don't loop TOO busily time.sleep(0.1) finally: call_fifo.close() feedback_fifo.close() if status: raise errors.ScriptletRunError( scriptlet_name=scriptlet_name, code=status )
def _run_scriptlet( self, scriptlet_name: str, scriptlet: str, workdir: str, env_generator: Callable[..., str] = common.assemble_env, ) -> None: if common.is_snap(): # Since the snap is classic, there is no $PATH pointing into the snap, which # means snapcraftctl won't be found. We can't use aliases since they don't # persist into subshells. However, we know that snapcraftctl lives in its own # directory, so adding that to the PATH should have no ill side effects. snapcraftctl_env = 'export PATH="$PATH:$SNAP/bin/scriptlet-bin"\n' else: snapcraftctl_env = "" with tempfile.TemporaryDirectory(dir=self._partdir) as tempdir: call_fifo = _NonBlockingRWFifo(os.path.join(tempdir, "function_call")) feedback_fifo = _NonBlockingRWFifo(os.path.join(tempdir, "call_feedback")) # snapcraftctl only works consistently if it's using the exact same # interpreter as that used by snapcraft itself, thus the definition # of SNAPCRAFT_INTERPRETER. script = textwrap.dedent( """\ set -e export SNAPCRAFTCTL_CALL_FIFO={call_fifo} export SNAPCRAFTCTL_FEEDBACK_FIFO={feedback_fifo} export SNAPCRAFT_INTERPRETER={interpreter} {snapcraftctl_env} {env} {scriptlet}""" ).format( interpreter=sys.executable, call_fifo=call_fifo.path, feedback_fifo=feedback_fifo.path, scriptlet=scriptlet, snapcraftctl_env=snapcraftctl_env, env=env_generator(), ) with tempfile.TemporaryFile(mode="w+") as script_file: print(script, file=script_file) script_file.flush() script_file.seek(0) process = subprocess.Popen(["/bin/sh"], stdin=script_file, cwd=workdir) status = None try: while status is None: function_call = call_fifo.read() if function_call: # Handle the function and let caller know that function # call has been handled (must contain at least a # newline, anything beyond is considered an error by # snapcraftctl) feedback_fifo.write( "{}\n".format( self._handle_builtin_function( scriptlet_name, function_call.strip() ) ) ) status = process.poll() # Don't loop TOO busily time.sleep(0.1) finally: call_fifo.close() feedback_fifo.close() if status: raise errors.ScriptletRunError( scriptlet_name=scriptlet_name, code=status )