feat(third_party/bazel): Check in rules_haskell from Tweag
This commit is contained in:
		
							parent
							
								
									2eb1dc26e4
								
							
						
					
					
						commit
						f723b8b878
					
				
					 479 changed files with 51484 additions and 0 deletions
				
			
		
							
								
								
									
										312
									
								
								third_party/bazel/rules_haskell/haskell/haddock.bzl
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								third_party/bazel/rules_haskell/haskell/haddock.bzl
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,312 @@ | |||
| """Haddock support""" | ||||
| 
 | ||||
| load("@bazel_skylib//lib:paths.bzl", "paths") | ||||
| load( | ||||
|     "@io_tweag_rules_haskell//haskell:providers.bzl", | ||||
|     "HaddockInfo", | ||||
|     "HaskellInfo", | ||||
|     "HaskellLibraryInfo", | ||||
| ) | ||||
| load(":private/context.bzl", "haskell_context", "render_env") | ||||
| load(":private/set.bzl", "set") | ||||
| 
 | ||||
| def _get_haddock_path(package_id): | ||||
|     """Get path to Haddock file of a package given its id. | ||||
| 
 | ||||
|     Args: | ||||
|       package_id: string, package id. | ||||
| 
 | ||||
|     Returns: | ||||
|       string: relative path to haddock file. | ||||
|     """ | ||||
|     return package_id + ".haddock" | ||||
| 
 | ||||
| def _haskell_doc_aspect_impl(target, ctx): | ||||
|     if HaskellInfo not in target or HaskellLibraryInfo not in target: | ||||
|         return [] | ||||
| 
 | ||||
|     # Packages imported via `//haskell:import.bzl%haskell_import` already | ||||
|     # contain an `HaddockInfo` provider, so we just forward it | ||||
|     if HaddockInfo in target: | ||||
|         return [] | ||||
| 
 | ||||
|     hs = haskell_context(ctx, ctx.rule.attr) | ||||
| 
 | ||||
|     package_id = target[HaskellLibraryInfo].package_id | ||||
|     html_dir_raw = "doc-{0}".format(package_id) | ||||
|     html_dir = ctx.actions.declare_directory(html_dir_raw) | ||||
|     haddock_file = ctx.actions.declare_file(_get_haddock_path(package_id)) | ||||
| 
 | ||||
|     # XXX Haddock really wants a version number, so invent one from | ||||
|     # thin air. See https://github.com/haskell/haddock/issues/898. | ||||
|     if target[HaskellLibraryInfo].version: | ||||
|         version = target[HaskellLibraryInfo].version | ||||
|     else: | ||||
|         version = "0" | ||||
| 
 | ||||
|     args = ctx.actions.args() | ||||
|     args.add("--package-name={0}".format(package_id)) | ||||
|     args.add("--package-version={0}".format(version)) | ||||
|     args.add_all([ | ||||
|         "-D", | ||||
|         haddock_file.path, | ||||
|         "-o", | ||||
|         html_dir.path, | ||||
|         "--html", | ||||
|         "--hoogle", | ||||
|         "--title={0}".format(package_id), | ||||
|         "--hyperlinked-source", | ||||
|     ]) | ||||
| 
 | ||||
|     transitive_haddocks = {} | ||||
|     transitive_html = {} | ||||
| 
 | ||||
|     for dep in ctx.rule.attr.deps: | ||||
|         if HaddockInfo in dep: | ||||
|             transitive_haddocks.update(dep[HaddockInfo].transitive_haddocks) | ||||
|             transitive_html.update(dep[HaddockInfo].transitive_html) | ||||
| 
 | ||||
|     for pid in transitive_haddocks: | ||||
|         args.add("--read-interface=../{0},{1}".format( | ||||
|             pid, | ||||
|             transitive_haddocks[pid].path, | ||||
|         )) | ||||
| 
 | ||||
|     prebuilt_deps = ctx.actions.args() | ||||
|     for dep in set.to_list(target[HaskellInfo].prebuilt_dependencies): | ||||
|         prebuilt_deps.add(dep.package) | ||||
|     prebuilt_deps.use_param_file(param_file_arg = "%s", use_always = True) | ||||
| 
 | ||||
|     compile_flags = ctx.actions.args() | ||||
|     for x in target[HaskellInfo].compile_flags: | ||||
|         compile_flags.add_all(["--optghc", x]) | ||||
|     compile_flags.add_all([x.path for x in set.to_list(target[HaskellInfo].source_files)]) | ||||
|     compile_flags.add("-v0") | ||||
| 
 | ||||
|     # haddock flags should take precedence over ghc args, hence are in | ||||
|     # last position | ||||
|     compile_flags.add_all(hs.toolchain.haddock_flags) | ||||
| 
 | ||||
|     locale_archive_depset = ( | ||||
|         depset([hs.toolchain.locale_archive]) if hs.toolchain.locale_archive != None else depset() | ||||
|     ) | ||||
| 
 | ||||
|     # TODO(mboes): we should be able to instantiate this template only | ||||
|     # once per toolchain instance, rather than here. | ||||
|     haddock_wrapper = ctx.actions.declare_file("haddock_wrapper-{}".format(hs.name)) | ||||
|     ctx.actions.expand_template( | ||||
|         template = ctx.file._haddock_wrapper_tpl, | ||||
|         output = haddock_wrapper, | ||||
|         substitutions = { | ||||
|             "%{ghc-pkg}": hs.tools.ghc_pkg.path, | ||||
|             "%{haddock}": hs.tools.haddock.path, | ||||
|             # XXX Workaround | ||||
|             # https://github.com/bazelbuild/bazel/issues/5980. | ||||
|             "%{env}": render_env(hs.env), | ||||
|         }, | ||||
|         is_executable = True, | ||||
|     ) | ||||
| 
 | ||||
|     # Transitive library dependencies for runtime. | ||||
|     trans_link_ctx = target[HaskellInfo].transitive_cc_dependencies.dynamic_linking | ||||
|     trans_libs = trans_link_ctx.libraries_to_link.to_list() | ||||
| 
 | ||||
|     ctx.actions.run( | ||||
|         inputs = depset(transitive = [ | ||||
|             set.to_depset(target[HaskellInfo].package_databases), | ||||
|             set.to_depset(target[HaskellInfo].interface_dirs), | ||||
|             set.to_depset(target[HaskellInfo].source_files), | ||||
|             target[HaskellInfo].extra_source_files, | ||||
|             set.to_depset(target[HaskellInfo].dynamic_libraries), | ||||
|             depset(trans_libs), | ||||
|             depset(transitive_haddocks.values()), | ||||
|             depset(transitive_html.values()), | ||||
|             target[CcInfo].compilation_context.headers, | ||||
|             depset([ | ||||
|                 hs.tools.ghc_pkg, | ||||
|                 hs.tools.haddock, | ||||
|             ]), | ||||
|             locale_archive_depset, | ||||
|         ]), | ||||
|         outputs = [haddock_file, html_dir], | ||||
|         mnemonic = "HaskellHaddock", | ||||
|         progress_message = "HaskellHaddock {}".format(ctx.label), | ||||
|         executable = haddock_wrapper, | ||||
|         arguments = [ | ||||
|             prebuilt_deps, | ||||
|             args, | ||||
|             compile_flags, | ||||
|         ], | ||||
|         use_default_shell_env = True, | ||||
|     ) | ||||
| 
 | ||||
|     transitive_html.update({package_id: html_dir}) | ||||
|     transitive_haddocks.update({package_id: haddock_file}) | ||||
| 
 | ||||
|     haddock_info = HaddockInfo( | ||||
|         package_id = package_id, | ||||
|         transitive_html = transitive_html, | ||||
|         transitive_haddocks = transitive_haddocks, | ||||
|     ) | ||||
|     output_files = OutputGroupInfo(default = transitive_html.values()) | ||||
| 
 | ||||
|     return [haddock_info, output_files] | ||||
| 
 | ||||
| haskell_doc_aspect = aspect( | ||||
|     _haskell_doc_aspect_impl, | ||||
|     attrs = { | ||||
|         "_haddock_wrapper_tpl": attr.label( | ||||
|             allow_single_file = True, | ||||
|             default = Label("@io_tweag_rules_haskell//haskell:private/haddock_wrapper.sh.tpl"), | ||||
|         ), | ||||
|     }, | ||||
|     attr_aspects = ["deps"], | ||||
|     toolchains = ["@io_tweag_rules_haskell//haskell:toolchain"], | ||||
| ) | ||||
| 
 | ||||
| def _haskell_doc_rule_impl(ctx): | ||||
|     hs = haskell_context(ctx) | ||||
| 
 | ||||
|     # Reject cases when number of dependencies is 0. | ||||
| 
 | ||||
|     if not ctx.attr.deps: | ||||
|         fail("haskell_doc needs at least one haskell_library component in deps") | ||||
| 
 | ||||
|     doc_root_raw = ctx.attr.name | ||||
|     haddock_dict = {} | ||||
|     html_dict_original = {} | ||||
|     all_caches = set.empty() | ||||
| 
 | ||||
|     for dep in ctx.attr.deps: | ||||
|         if HaddockInfo in dep: | ||||
|             html_dict_original.update(dep[HaddockInfo].transitive_html) | ||||
|             haddock_dict.update(dep[HaddockInfo].transitive_haddocks) | ||||
|         if HaskellInfo in dep: | ||||
|             set.mutable_union( | ||||
|                 all_caches, | ||||
|                 dep[HaskellInfo].package_databases, | ||||
|             ) | ||||
| 
 | ||||
|     # Copy docs of Bazel deps into predefined locations under the root doc | ||||
|     # directory. | ||||
| 
 | ||||
|     html_dict_copied = {} | ||||
|     doc_root_path = "" | ||||
| 
 | ||||
|     for package_id in html_dict_original: | ||||
|         html_dir = html_dict_original[package_id] | ||||
|         output_dir = ctx.actions.declare_directory( | ||||
|             paths.join( | ||||
|                 doc_root_raw, | ||||
|                 package_id, | ||||
|             ), | ||||
|         ) | ||||
|         doc_root_path = paths.dirname(output_dir.path) | ||||
| 
 | ||||
|         html_dict_copied[package_id] = output_dir | ||||
| 
 | ||||
|         ctx.actions.run_shell( | ||||
|             inputs = [html_dir], | ||||
|             outputs = [output_dir], | ||||
|             command = """ | ||||
|       mkdir -p "{doc_dir}" | ||||
|       # Copy Haddocks of a dependency. | ||||
|       cp -R -L "{html_dir}/." "{target_dir}" | ||||
|       """.format( | ||||
|                 doc_dir = doc_root_path, | ||||
|                 html_dir = html_dir.path, | ||||
|                 target_dir = output_dir.path, | ||||
|             ), | ||||
|         ) | ||||
| 
 | ||||
|     # Do one more Haddock call to generate the unified index | ||||
| 
 | ||||
|     index_root_raw = paths.join(doc_root_raw, "index") | ||||
|     index_root = ctx.actions.declare_directory(index_root_raw) | ||||
| 
 | ||||
|     args = ctx.actions.args() | ||||
|     args.add_all([ | ||||
|         "-o", | ||||
|         index_root.path, | ||||
|         "--title={0}".format(ctx.attr.name), | ||||
|         "--gen-index", | ||||
|         "--gen-contents", | ||||
|     ]) | ||||
| 
 | ||||
|     if ctx.attr.index_transitive_deps: | ||||
|         # Include all packages in the unified index. | ||||
|         for package_id in html_dict_copied: | ||||
|             args.add("--read-interface=../{0},{1}".format( | ||||
|                 package_id, | ||||
|                 haddock_dict[package_id].path, | ||||
|             )) | ||||
|     else: | ||||
|         # Include only direct dependencies. | ||||
|         for dep in ctx.attr.deps: | ||||
|             if HaddockInfo in dep: | ||||
|                 package_id = dep[HaddockInfo].package_id | ||||
|                 args.add("--read-interface=../{0},{1}".format( | ||||
|                     package_id, | ||||
|                     haddock_dict[package_id].path, | ||||
|                 )) | ||||
| 
 | ||||
|     for cache in set.to_list(all_caches): | ||||
|         args.add("--optghc=-package-db={0}".format(cache.dirname)) | ||||
| 
 | ||||
|     locale_archive_depset = ( | ||||
|         depset([hs.toolchain.locale_archive]) if hs.toolchain.locale_archive != None else depset() | ||||
|     ) | ||||
| 
 | ||||
|     ctx.actions.run( | ||||
|         inputs = depset(transitive = [ | ||||
|             set.to_depset(all_caches), | ||||
|             depset(html_dict_copied.values()), | ||||
|             depset(haddock_dict.values()), | ||||
|             locale_archive_depset, | ||||
|         ]), | ||||
|         outputs = [index_root], | ||||
|         mnemonic = "HaskellHaddockIndex", | ||||
|         executable = hs.tools.haddock, | ||||
|         arguments = [args], | ||||
|     ) | ||||
| 
 | ||||
|     return [DefaultInfo( | ||||
|         files = depset(html_dict_copied.values() + [index_root]), | ||||
|     )] | ||||
| 
 | ||||
| haskell_doc = rule( | ||||
|     _haskell_doc_rule_impl, | ||||
|     attrs = { | ||||
|         "deps": attr.label_list( | ||||
|             aspects = [haskell_doc_aspect], | ||||
|             doc = "List of Haskell libraries to generate documentation for.", | ||||
|         ), | ||||
|         "index_transitive_deps": attr.bool( | ||||
|             default = False, | ||||
|             doc = "Whether to include documentation of transitive dependencies in index.", | ||||
|         ), | ||||
|     }, | ||||
|     toolchains = ["@io_tweag_rules_haskell//haskell:toolchain"], | ||||
| ) | ||||
| """Create API documentation. | ||||
| 
 | ||||
| Builds API documentation (using [Haddock][haddock]) for the given | ||||
| Haskell libraries. It will automatically build documentation for any | ||||
| transitive dependencies to allow for cross-package documentation | ||||
| linking. | ||||
| 
 | ||||
| Example: | ||||
|   ```bzl | ||||
|   haskell_library( | ||||
|     name = "my-lib", | ||||
|     ... | ||||
|   ) | ||||
| 
 | ||||
|   haskell_doc( | ||||
|     name = "my-lib-doc", | ||||
|     deps = [":my-lib"], | ||||
|   ) | ||||
|   ``` | ||||
| 
 | ||||
| [haddock]: http://haskell-haddock.readthedocs.io/en/latest/ | ||||
| """ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue