Skip to content
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

How to use the externalize option? #676

Open
mgoonde opened this issue Nov 15, 2024 · 16 comments
Open

How to use the externalize option? #676

mgoonde opened this issue Nov 15, 2024 · 16 comments

Comments

@mgoonde
Copy link

mgoonde commented Nov 15, 2024

Hello,
I have a code with a plugin system, where the base code's documentation is written by FORD, but the plugin documentations are separate ford projects not included in the base. I would like to use the externalize option on the base code, to be able to reference it from the documentation of the plugins (also written by FORD).

I tried setting externalize: true in the base code, and then giving external: local=/path/to/base/doc in the project file of a plugin documentation. But upon running FORD for my plugin, i get the error:

  Correlating information from different parts of your project...
Traceback (most recent call last):
  File "/home/mgunde/.local/bin/ford", line 8, in <module>
    sys.exit(run())
  File "/home/mgunde/.local/lib/python3.10/site-packages/ford/__init__.py", line 489, in run
    main(proj_data, proj_docs)
  File "/home/mgunde/.local/lib/python3.10/site-packages/ford/__init__.py", line 420, in main
    project.correlate()
  File "/home/mgunde/.local/lib/python3.10/site-packages/ford/fortran_project.py", line 340, in correlate
    container.correlate(self)
  File "/home/mgunde/.local/lib/python3.10/site-packages/ford/sourceform.py", line 1273, in correlate
    dtype.correlate(project)
  File "/home/mgunde/.local/lib/python3.10/site-packages/ford/sourceform.py", line 2069, in correlate
    proc.correlate(project)
  File "/home/mgunde/.local/lib/python3.10/site-packages/ford/sourceform.py", line 2485, in correlate
    if self.proto:
AttributeError: 'ExternalBoundProcedure' object has no attribute 'proto'

Without the external: local=... specification, the documentation writes normally (however giving warnings that references are not found, which is normal i guess).

Is this an error due to my wrong use of the external option, or something else?

thanks

@haraldkl
Copy link
Contributor

It sounds to me like you are using it as intended.

It may be that the externalize function does not properly deal with type-bound procedures. I guess, those are not tested in that setting.

@haraldkl
Copy link
Contributor

The test for external projects does contain a type bound procedure:

  type, abstract, public :: solverAborts_type
  contains
    procedure(load_aborts), deferred :: load
  end type solverAborts_type

Can you share some sample code that causes the error?

@haraldkl
Copy link
Contributor

Does #677 help?

@mgoonde
Copy link
Author

mgoonde commented Nov 16, 2024

Can you share some sample code that causes the error?

Sure thing!

This is my fundamental class, called pointers, defined in a module file of the base code:

module gc_pointers_h
  implicit none

  private
  public :: pointers

  type :: pointers
   contains
    procedure :: check
  end type pointers

  interface pointers
    module procedure pointers_constructor
  end interface pointers

  interface
     module function pointers_constructor( gencat )result( this )
      class(*), pointer, intent( in ) :: gencat
      type( pointers ) :: this
    end function pointers_constructor

    module subroutine check( self )
      class( pointers ), intent( in ) :: self
    end subroutine check
  end interface

 CONTAINS

end module gc_pointers_h

Then, the base code specifies a t_method class, which extends pointers:

module gc_method_h

  use gc_pointers_h, only: pointers
  implicit none

  private
  public :: t_method, method_constructor

  type, extends(pointers) :: t_method
   contains
     procedure :: init, run
  end type t_method

  abstract interface
     function method_constructor(ptr, nwords, words) result(this)
       import t_method
       class(t_method), pointer :: this
       class(*), intent(in) :: ptr
       integer, intent(in) :: nwords
       character(*), intent(in) :: words(:)
     end function method_constructor
  end interface

CONTAINS

  function init(self) result(ierr)
    class(t_method), intent(inout) :: self
    integer :: ierr
  end function init

  function run(self) result(ierr)
    class(t_method), intent(inout) :: self
    integer :: ierr
  end function run

end module gc_method_h

And finally, in my plugin code i create an extension of the t_method:

module gc_method_fks_h
  use gc_method_h, only : t_method
  implicit none

  public :: t_method_fks, method_fks_constructor
  private

  !> |cmd| `method fks`
  type, extends( t_method ) :: t_method_fks
   contains
     procedure :: init => method_fks_init
     procedure :: run => method_fks_run
     final :: method_fks_destroy
  end type t_method_fks

  interface method_fks_
     module procedure :: method_fks_constructor
  end interface method_fks_

  interface
     module function method_fks_constructor( ptr, nwords, words )result( this )
      class( t_method ), pointer :: this
      class(*),        intent(in) :: ptr
      integer,         intent( in ) :: nwords
      character(*),    intent( in ) :: words(:)
    end function method_fks_constructor

    module function method_fks_init( self )result(ierr)
      class( t_method_fks ), intent( inout ) :: self
      integer :: ierr
    end function method_fks_init

    module function method_fks_run( self )result(ierr)
      class( t_method_fks ), intent( inout ) :: self
      integer :: ierr
    end function method_fks_run

    module subroutine method_fks_destroy( self )
      type( t_method_fks ), intent(inout) :: self
    end subroutine method_fks_destroy
  end interface

CONTAINS

end module gc_method_fks_h

I managed to track down something, when i comment out the procedure :: check in the definition of type pointers (the first file above), FORD runs without error, and the final documentation properly links the two projects.

@mgoonde
Copy link
Author

mgoonde commented Nov 16, 2024

Does #677 help?

It does not.

@haraldkl
Copy link
Contributor

Thanks! I can reproduce the bug and will have a look.

haraldkl added a commit to haraldkl/ford that referenced this issue Nov 16, 2024
@haraldkl
Copy link
Contributor

I've now pushed some changes to #677, which makes it work for me. Can you please give it a try?

@mgoonde
Copy link
Author

mgoonde commented Nov 16, 2024

Yeah that seems to work. Awesome, thanks!

There seems to be some small inconsistency however, in that the directory specified in external: local=... seems relative to the location from which i launch FORD, while for comparison the output_dir=.... seems relative to the location of the ford project file.

@haraldkl
Copy link
Contributor

In 6fe3fb2 I now changed the external definition to be relative to the project directory (if it is relative).

@mgoonde
Copy link
Author

mgoonde commented Nov 18, 2024

That seems to work for me, thanks!

Is it possible to have several paths for the external: local=.... command?

Also, is it desirable to have a hard-stop error when the needed file modules.json is not found? Could it just print a warning and continue without it?

@haraldkl
Copy link
Contributor

Is it possible to have several paths for the external: local=.... command?

Yes. See for example the Ateles source repository.

Could it just print a warning and continue without it?

It could, but that may well go unnoticed. I think, if it was requested by the user in the project, it is justified to abort when that database isn't found.

@mgoonde
Copy link
Author

mgoonde commented Nov 18, 2024

Yes. See for example the Ateles source repository.

Nice, thanks!

It could, but that may well go unnoticed. I think, if it was requested by the user in the project, it is justified to abort when that database isn't found.

Yes, understandable.

How can I pass the command via the command line --config? I tried with quoting the value in the pair, like: --config="external='local=/path'" but that doesn't work.

@haraldkl
Copy link
Contributor

This should work with:

ford -L 'local = /path'

@mgoonde
Copy link
Author

mgoonde commented Nov 18, 2024

Is it possible to add the external modules into the search index?

@haraldkl
Copy link
Contributor

This may depend on the extend of what to add to the search. The search iterates over the generated pages and adds them to the search. Adding all pages from the subprojects into the search appears a little counterproductive to me. I guess it would be possible to iterate over all Fortran objects that have an external_url attribute, to add them to the search additionally, but the question then is what needs to be added as searched text for them.

@mgoonde
Copy link
Author

mgoonde commented Nov 18, 2024

I'm not really sure how this works, but if the search index is generated in a subproject, it could be added to the search of the project which references it as external, since it's already generated in a file anyway. So kind of multiple search index files. If that is possible.

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

No branches or pull requests

2 participants