diff --git a/aws_lambda_builders/workflows/custom_make/actions.py b/aws_lambda_builders/workflows/custom_make/actions.py index 67579a74a..e689012e5 100644 --- a/aws_lambda_builders/workflows/custom_make/actions.py +++ b/aws_lambda_builders/workflows/custom_make/actions.py @@ -22,7 +22,16 @@ class CustomMakeAction(BaseAction): DESCRIPTION = "Running build target on Makefile" PURPOSE = Purpose.COMPILE_SOURCE - def __init__(self, artifacts_dir, scratch_dir, manifest_path, osutils, subprocess_make, build_logical_id): + def __init__( + self, + artifacts_dir, + scratch_dir, + manifest_path, + osutils, + subprocess_make, + build_logical_id, + working_directory=None, + ): """ :type artifacts_dir: str :param artifacts_dir: directory where artifacts needs to be stored. @@ -38,6 +47,13 @@ def __init__(self, artifacts_dir, scratch_dir, manifest_path, osutils, subproces :type subprocess_make aws_lambda_builders.workflows.custom_make.make.SubprocessMake :param subprocess_make: An instance of the Make process wrapper + + :type build_logical_id: str + :param build_logical_id: the lambda resource logical id that will be built by the custom action. + + :type working_directory: str + :param working_directory: path to the working directory where the Makefile will be executed. Use the scratch_dir + as the working directory if the input working_directory is None """ super(CustomMakeAction, self).__init__() self.artifacts_dir = artifacts_dir @@ -46,6 +62,7 @@ def __init__(self, artifacts_dir, scratch_dir, manifest_path, osutils, subproces self.osutils = osutils self.subprocess_make = subprocess_make self.build_logical_id = build_logical_id + self.working_directory = working_directory if working_directory else scratch_dir @property def artifact_dir_path(self): @@ -91,7 +108,7 @@ def execute(self): "build-{logical_id}".format(logical_id=self.build_logical_id), ], env=current_env, - cwd=self.scratch_dir, + cwd=self.working_directory, ) except MakeExecutionError as ex: raise ActionFailedError(str(ex)) diff --git a/aws_lambda_builders/workflows/custom_make/workflow.py b/aws_lambda_builders/workflows/custom_make/workflow.py index 04c680237..627125667 100644 --- a/aws_lambda_builders/workflows/custom_make/workflow.py +++ b/aws_lambda_builders/workflows/custom_make/workflow.py @@ -34,6 +34,7 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim # Find the logical id of the function to be built. options = kwargs.get("options") or {} build_logical_id = options.get("build_logical_id", None) + working_directory = options.get("working_directory", scratch_dir) if not build_logical_id: raise WorkflowFailedError( @@ -51,6 +52,7 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim osutils=self.os_utils, subprocess_make=subprocess_make, build_logical_id=build_logical_id, + working_directory=working_directory, ) self.actions = [CopySourceAction(source_dir, scratch_dir, excludes=self.EXCLUDED_FILES), make_action] diff --git a/tests/integration/workflows/custom_make/test_custom_make.py b/tests/integration/workflows/custom_make/test_custom_make.py index 1f641178e..4bd31bef0 100644 --- a/tests/integration/workflows/custom_make/test_custom_make.py +++ b/tests/integration/workflows/custom_make/test_custom_make.py @@ -57,6 +57,48 @@ def test_must_build_python_project_through_makefile(self): output_files = set(os.listdir(self.artifacts_dir)) self.assertEqual(expected_files, output_files) + def test_build_python_project_failed_through_makefile_no_python_source_in_default_working_directory(self): + source_code = os.path.join(os.path.dirname(__file__), "testdata", "makefile-in-different-working-directory") + manifest_path_valid = os.path.join(source_code, "Makefile") + with self.assertRaises(WorkflowFailedError): + self.builder.build( + source_code, + self.artifacts_dir, + self.scratch_dir, + manifest_path_valid, + runtime=self.runtime, + options={"build_logical_id": "HelloWorldFunction"}, + ) + + def test_must_build_python_project_through_makefile_with_custom_working_directory(self): + source_code = os.path.join(os.path.dirname(__file__), "testdata", "makefile-in-different-working-directory") + manifest_path_valid = os.path.join(source_code, "Makefile") + working_directory = os.path.join(source_code, "source_code") + self.builder.build( + source_code, + self.artifacts_dir, + self.scratch_dir, + manifest_path_valid, + runtime=self.runtime, + options={"build_logical_id": "HelloWorldFunction", "working_directory": working_directory}, + ) + dependencies_installed = { + "chardet", + "urllib3", + "idna", + "urllib3-1.25.11.dist-info", + "chardet-3.0.4.dist-info", + "certifi-2020.4.5.2.dist-info", + "certifi", + "idna-2.10.dist-info", + "requests", + "requests-2.23.0.dist-info", + } + + expected_files = self.test_data_files.union(dependencies_installed) + output_files = set(os.listdir(self.artifacts_dir)) + self.assertEqual(expected_files, output_files) + def test_must_build_python_project_through_makefile_unknown_target(self): with self.assertRaises(WorkflowFailedError): self.builder.build( diff --git a/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/Makefile b/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/Makefile new file mode 100644 index 000000000..0133698ab --- /dev/null +++ b/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/Makefile @@ -0,0 +1,5 @@ +build-HelloWorldFunction: + cp *.py $(ARTIFACTS_DIR) + cp requirements-requests.txt $(ARTIFACTS_DIR) + python -m pip install -r requirements-requests.txt -t $(ARTIFACTS_DIR) + rm -rf $(ARTIFACTS_DIR)/bin diff --git a/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/__init__.py b/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/main.py b/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/main.py new file mode 100644 index 000000000..dc2b47454 --- /dev/null +++ b/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/main.py @@ -0,0 +1,6 @@ +import requests + + +def lambda_handler(event, context): + # Just return the requests version. + return "{}".format(requests.__version__) diff --git a/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/requirements-requests.txt b/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/requirements-requests.txt new file mode 100644 index 000000000..a0111f46d --- /dev/null +++ b/tests/integration/workflows/custom_make/testdata/makefile-in-different-working-directory/source_code/requirements-requests.txt @@ -0,0 +1,4 @@ +requests==2.23.0 + +# Pinning so the test can expect a given version +certifi==2020.4.5.2 # dep of requests \ No newline at end of file diff --git a/tests/unit/workflows/custom_make/test_actions.py b/tests/unit/workflows/custom_make/test_actions.py index 7bcc4d83f..6f1bdd432 100644 --- a/tests/unit/workflows/custom_make/test_actions.py +++ b/tests/unit/workflows/custom_make/test_actions.py @@ -43,6 +43,32 @@ def test_call_makefile_target(self, OSUtilMock, SubprocessMakeMock): ["--makefile", "manifest", "build-logical_id"], cwd="scratch_dir", env=ANY ) + @patch("aws_lambda_builders.workflows.custom_make.utils.OSUtils") + @patch("aws_lambda_builders.workflows.custom_make.make.SubProcessMake") + def test_call_makefile_target_with_working_directory(self, OSUtilMock, SubprocessMakeMock): + osutils = OSUtilMock.return_value + subprocess_make = SubprocessMakeMock.return_value + + action = CustomMakeAction( + "artifacts", + "scratch_dir", + "manifest", + osutils=osutils, + subprocess_make=subprocess_make, + build_logical_id="logical_id", + working_directory="working_dir", + ) + + osutils.dirname.side_effect = lambda value: "/dir:{}".format(value) + osutils.abspath.side_effect = lambda value: "/abs:{}".format(value) + osutils.joinpath.side_effect = lambda a, b: "{}/{}".format(a, b) + + action.execute() + + subprocess_make.run.assert_called_with( + ["--makefile", "manifest", "build-logical_id"], cwd="working_dir", env=ANY + ) + @patch("aws_lambda_builders.workflows.custom_make.utils.OSUtils") @patch("aws_lambda_builders.workflows.custom_make.make.SubProcessMake") def test_makefile_target_fails(self, OSUtilMock, SubprocessMakeMock): diff --git a/tests/unit/workflows/custom_make/test_workflow.py b/tests/unit/workflows/custom_make/test_workflow.py index 8775c11b0..39f58abcd 100644 --- a/tests/unit/workflows/custom_make/test_workflow.py +++ b/tests/unit/workflows/custom_make/test_workflow.py @@ -1,4 +1,5 @@ from unittest import TestCase +from unittest.mock import patch from aws_lambda_builders.architecture import X86_64, ARM64 from aws_lambda_builders.actions import CopySourceAction @@ -46,3 +47,20 @@ def test_must_validate_architecture(self): self.assertEqual(workflow.architecture, "x86_64") self.assertEqual(workflow_with_arm.architecture, "arm64") + + def test_use_scratch_dir_as_working_directory(self): + workflow = CustomMakeWorkflow( + "source", "artifacts", "scratch_dir", "manifest", options={"build_logical_id": "hello"} + ) + self.assertEqual(workflow.actions[1].working_directory, "scratch_dir") + + def test_use_working_directory(self): + workflow = CustomMakeWorkflow( + "source", + "artifacts", + "scratch_dir", + "manifest", + options={"build_logical_id": "hello", "working_directory": "working/dir/path"}, + ) + + self.assertEqual(workflow.actions[1].working_directory, "working/dir/path")