Skip to content

feat: Makefile based builder for provided runtimes #166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 7, 2020

Conversation

sriram-mv
Copy link
Contributor

@sriram-mv sriram-mv commented Apr 10, 2020

Why is this change necessary?

  • provided runtimes are runtimes that are custom built with own
    bootstraps etc, and its hard to determine which language it actually is.
    A generic makefile based builder gives the control back to the user on
    how the project needs to be built.

How does it address the issue?

  • Provide a way to build for provided runtimes, which does not exist
    today.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Why is this change necessary?

* provided runtimes are runtimes that are custom built with own
bootstraps etc, and its hard to determine which language it actually is.
A generic makefile based builder gives the control back to the user on
how the project needs to be built.

How does it address the issue?

* Provide a way to build for provided runtimes, which does not exist
today.
* Which environment variables are usable in this makefile?
* There are a series of whitelisted environment variables that need to be defined and not be overriden within the Makefile to work. Currently that is just `$ARTIFACTS_DIR`

* Can this be used even for runtimes that have builders associated with it? eg: python3.8?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably something we need to address in the design now. Even if it is not supported immediately, how will it be added.

Perhaps an optional build parameter where you can specify a Makefile explicitly, which then overrides any auto-detected build process? I can see though how this could be a problem across many functions, but that's a starting point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact there are complexities may be a reason to come up with an answer before shipping this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@awood45 This is something that should be considered in SAM CLI not lambda builders. Lambda builders is a library of builders that someone can run, how SAM CLI picks which one is completely within SAM CLI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this would be choosing a workflow based on the manifest in SAM CLI. for eg: if a makefile is present and a build target is present, we choose the makefile workflow, else if we choose the bundled the workflow.

The logic to choose an appropriate workflow would need to be added there.

https://github.com/awslabs/aws-sam-cli/blob/develop/samcli/lib/build/workflow_config.py#L137

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even in sam-cli do we need to provide use of makefile for known workflows. I understand it is adding one more feature with some flexibility. But is it needed? What problem will this be solving? I think it'll add another case which then need lot of testing to make sure we didn't miss an edge case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think it would be opting on a makefile workflow if necessary. Not neccesarily forced. if you are using a makefile based build approach, you would be responsible for the build instead.

@sriram-mv sriram-mv marked this pull request as draft April 14, 2020 21:08
@sriram-mv sriram-mv force-pushed the provided_builder branch 6 times, most recently from 0ccce7b to b7080bf Compare April 15, 2020 17:22
Why is this change necessary?

* Tests to ensure that the makefile based approach for provided runtimes
works.
@sriram-mv sriram-mv force-pushed the provided_builder branch 3 times, most recently from 5634fe5 to 0e0d1a9 Compare April 15, 2020 22:18
Why is this change necessary?

* On Windows, certain env variables are required for functioning of
binaries within the Makefile

How does it address the issue?

* pass current environmental variables along with new ones over to the
subprocess runner.

What side effects does this change have?

* None
@sriram-mv sriram-mv force-pushed the provided_builder branch 2 times, most recently from 3658e58 to 8698121 Compare April 16, 2020 17:17
@sriram-mv sriram-mv force-pushed the provided_builder branch 2 times, most recently from 59ae79a to 20d786c Compare April 16, 2020 20:46
Why is this change necessary?

* on powershell on windows vs git bash on windows, paths are treated
differently.

How does it address the issue?

* Dynamically change path by looking up `sh`. If present, its unix path
else its windows paths.

What side effects does this change have?

* `sh` is the check which determines which path to use. If `sh` aint
present, this will end up in breaking the workflow.
@sriram-mv sriram-mv marked this pull request as ready for review April 16, 2020 21:15
@sriram-mv sriram-mv changed the title [WIP] feat: Makefile based builder for provided runtimes feat: Makefile based builder for provided runtimes Apr 16, 2020
Sample Makefile:

````
build-HelloWorldFunction:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be HelloWorldFunction-build?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is how the design and implementation are designed at this point. Is there a reason you prefer it the other way around?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope in line 21 it says {Function_Logical_Id]-build that is why I said.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

* Which environment variables are usable in this makefile?
* There are a series of whitelisted environment variables that need to be defined and not be overriden within the Makefile to work. Currently that is just `$ARTIFACTS_DIR`

* Can this be used even for runtimes that have builders associated with it? eg: python3.8?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even in sam-cli do we need to provide use of makefile for known workflows. I understand it is adding one more feature with some flexibility. But is it needed? What problem will this be solving? I think it'll add another case which then need lot of testing to make sure we didn't miss an edge case.

def _artifact_dir_path(self):
# This is required when running on windows to determine if we are running in linux
# subsystem or on native cmd or powershell.
return Path(self.artifacts_dir).as_posix() if self.osutils.which("sh") else self.artifacts_dir
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this consider other types of shells? like zsh, csh etc? On that note, I think we should have special case for windows environment, which have rather low number of shells. Linux have lot of shells.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good point, we can check if we are on windows before we start looking for any shell.

LOG.debug("executing Make: %s", invoke_make)

p = self.osutils.popen(
invoke_make, stdout=self.osutils.pipe, stderr=self.osutils.pipe, cwd=cwd, env=env if env else {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why don't we default env to {} instead of None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure,we can.


LOG.debug("executing Make: %s", invoke_make)

p = self.osutils.popen(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have noticed that we have an OSUtil for os specific calls. Should have a POpenUtil for open specific calls with consistent logging around the actual call for all workflows? It'll also avoid some code duplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably. but the osutils are desinged in a manner such that each is independent, so that each workflow can in essence be its own module. But open to considering it.

Why is this change necessary?

* on unix, its not necessary to transform the artifacts_dir path.

How does it address the issue?

* Do a check if we are windows, before attempting to look for binaries.

What side effects does this change have?

* None.
Copy link
Contributor

@jfuss jfuss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks really good. Nothing major in my feedback, mostly just small things or suggestions.

When the makefile is called through a lambda builder workflow, the appropriate target is triggered and artifacts should be copied to the exposed environmental variable `$ARTIFACTS_DIR`. This environment variable is seeded by the lambda builder with a value set to it. The targets are defined as

```
build-{Function_Logical_Id}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a great place to start, but think customers will be asking for a make build target as well. This would allow additional support for common builds between functions and remove the need to maintain a build for each Logical Id.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its easy to setup such a target by the user themselves and throw common stuff there. if there is a general target it becomes hard to do logical separation from building perspective, especially when multiple functions might share the same Makefile.

LOG = logging.getLogger(__name__)


class ProvidedMakeAction(BaseAction):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be labeling this Provided? I can see us wanting to expand this out to other runtimes, like Go/Python/etc in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can rename it to just custom. its more generic that way.

:type osutils: aws_lambda_builders.workflows.provided_make.utils.OSUtils
:param osutils: An instance of OS Utilities for file manipulation

:type subprocess_make aws_lambda_builders.workflows.provided_make.make.SubprocessMake
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot build_logical_id doc strings.

self.subprocess_make = subprocess_make
self.build_logical_id = build_logical_id

def _artifact_dir_path(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be a @property?

:type args: list
:param args: Command line arguments to pass to Make

:type args: dict
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be env?

Suggested change
:type args: dict
:type env: dict

from aws_lambda_builders.utils import which


class OSUtils(object):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The famous Copy-paste OSUtils. We should make a generic one eventually

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. we should.

return p

def environ(self):
return os.environ
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a os.environ.copy()? That way we don't risk mutating customers env var by accident? Some workflows already do this (like Go Dep for example).


# Find the logical id of the function to be built.
options = kwargs.get("options") or {}
build_logical_id = options.get("build_logical_id", None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when this is not provided?

Should we just error out here if it is a requirement of the builder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we would error out saying that the required make file target was not present.

self.assertEquals(expected_files, output_files)

def test_must_build_python_project_through_makefile_unknown_target(self):
with self.assertRaises(WorkflowFailedError):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a way to make this more specific of an Error?


class TestProvidedMakeAction(TestCase):
def setUp(self):
self.original_env = os.environ
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.copy()?

c2tarun
c2tarun previously approved these changes Apr 21, 2020
Why is this change necessary?

* Without this, the make manifest file is always assumed to be present
with the source_dir.

How does it address the issue?

* An explicitly passed in manifest file path is now not ignored.

What side effects does this change have?

* None.
self.osutils.makedirs(self.artifacts_dir)

try:
current_env = self.osutils.environ().copy()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just do this in OSUtils so we don't have to remember to copy anytime we access the self.osutils.environ().

jfuss
jfuss previously approved these changes May 7, 2020
@sriram-mv sriram-mv merged commit d47d42a into aws:develop May 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants