Issue title: `generate_function` should not generate files on storage automatically
 Issue body: **Is your feature request related to a problem? Please describe.**
Could `generate_function` instead of creating files just return their source code? It feels strange that the files on storage are generated, since if a user needs a file, he can easily save it himself.

**Describe the solution you'd like**
1. a property for accessing sources. Made of objects of `GeneratedSource` class, each one contains the properties
* `path` of `PurePath` with a path of the file rel to the output dir
* `warnings` - an array of warjings
* `source` - source code
2. `generated_files` lazily take `source` of corresponding fikes qnd write them to disk. Usage of `generated_files` causes a deprecation warning to be emitted.

**Describe alternatives you've considered**
No.

 Comments: 
 Comment 0: Yeah I think this is a good idea - probably wouldn't deprecate the option of having symforce write to disk but would be good to have the option not to.  One question would be whether all other tools we call out to are ok or can be made to work with this (e.g. autoformatters, skymarshal).

What are some examples of warnings to include in point 1 bullet 2?  Not sure what you mean there (or if there are some warnings we currently emit in other forms that this would be replacing)
 Comment 1: >What are some examples of warnings to include in point 1 bullet 2? Not sure what you mean there (or if there are some warnings we currently emit in other forms that this would be replacing)

I guess no for now, but in future it may make sense to wind up an own DSL (probably DAG-of-`dict`s-based (can be serialized/parsed into/from many formats (s.a. CBOR, terraform tf2, json, NEON and so on), but YAML is usually used), as lot of projects do). Some projects doing that have a model that 1 input file corresponds to 1 output file. Then for 1 input compiler generates 1 output + bunch of warnings, if any, or, in the case of an error, just `null` output + bunch of errors. Then it can be either written to disk using the attached path, or just used disklessly, for example by showing to a user, or just disklessly compiling and executing.
{'source': 'GithubIssues', 'repo_name_with_owner': 'symforce-org/symforce', 'issue_id': 1265658876, 'repo_stars': 930.0}